Skip to content

fix(keychain): surface real broadcast errors and retry via a known-good node#823

Merged
feruzm merged 7 commits into
developfrom
bugfix/keychain-broadcast-error-and-fallback
May 23, 2026
Merged

fix(keychain): surface real broadcast errors and retry via a known-good node#823
feruzm merged 7 commits into
developfrom
bugfix/keychain-broadcast-error-and-fallback

Conversation

@feruzm
Copy link
Copy Markdown
Member

@feruzm feruzm commented May 22, 2026

Problem

Promoting a post (or any active-authority op) signed with a browser extension could fail in the console with:

Uncaught (in promise) Error: Extension broadcast failed

Two layered issues:

  1. The real reason was discarded. When Keychain/Hive Keeper/Peak Vault returned success: false, broadcastViaKeychain threw a hardcoded "Extension broadcast failed" and ignored the extension's actual message/error (node error, missing key, RC, or a plain user-cancel). The TxResponse type only declared { success, result }, so those fields weren't even visible.
  2. It defeated the SDK auth-upgrade fallback. The SDK decides whether to prompt for active authority by string-matching the error message (parseChainError/missing active authority/i). A generic string matches nothing, so a recoverable auth failure hard-failed instead of showing the upgrade dialog.
  3. It surfaced as an unhandled rejection. The Promote modal awaited mutateAsync with no try/catch.

Fix

  • Extend TxResponse with the optional message/error fields the extensions actually return.
  • Add a shared extensionErrorMessage() helper and surface the real reason in the unified hive-extensions layer and the legacy keychain.ts call sites.
  • broadcastViaKeychain now retries a failed broadcast once through the first (preferred) node in the curated public-nodes.json, so a flaky/outaged default node (e.g. the common api.hive.blog default during maintenance) no longer hard-blocks the user. Preserves the user's own Keychain node on the first attempt; the retry is gated by isUserCancellation() so a cancel never re-opens the popup, and only runs when the caller didn't pin a node.
  • Promote modal: catch the rejection (no more Uncaught (in promise)) and show the reason inline.
  • Unit tests for extensionErrorMessage / isUserCancellation.

Tradeoff to note

The fallback re-calls requestBroadcast, which re-opens the extension confirmation popup. So in the (rare) genuine node-failure case the user approves a second time. Cancels and the happy path are unaffected. Full node hangs still surface as the existing timeout without a retry.

Summary by CodeRabbit

  • New Features

    • Clearer error messages for keychain/broadcast failures and retry via a known-good node when appropriate.
    • Centralized post-transaction cache refreshes for more consistent UI updates.
    • Broadcasts now default to returning after mempool acceptance (faster feedback).
  • Bug Fixes

    • Prevented unhandled promise rejections in promote/transaction flows.
    • Improved detection and handling of user-cancelled operations.
  • Tests

    • Added tests for error-message parsing, cancellation detection, and retryable node errors.

Review Change Stack

…od node

Extension broadcast failures threw a hardcoded "Extension broadcast failed",
discarding the extension's actual message/error. That hid the real cause and,
because the SDK classifies auth errors by string-matching the message, defeated
the auth-upgrade fallback (a generic string never matches "missing active
authority").

- Extend TxResponse with the optional message/error fields the extensions
  actually return.
- Surface them via a shared extensionErrorMessage() helper, used by the unified
  hive-extensions layer and the legacy keychain.ts call sites.
- broadcastViaKeychain retries a failed broadcast once through the first node in
  the curated public-nodes.json, so a flaky/outaged default node (e.g. the
  common api.hive.blog default during maintenance) no longer hard-blocks the
  user. Gated via isUserCancellation() so a user-cancel never re-opens the
  popup, and only when the caller didn't pin a node.
- Promote modal: catch the mutateAsync rejection so a failed/declined sign no
  longer surfaces as an "Uncaught (in promise)", and show the reason inline.
- Add unit tests for extensionErrorMessage / isUserCancellation.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6378c87687

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/src/utils/hive-extensions.ts Outdated
Comment on lines +377 to +378
if (fallbackNode && !isUserCancellation(response)) {
return attempt(fallbackNode).then((retry) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry fallback only for node/transport failures

The retry path is currently triggered for every non-cancellation broadcast failure, so deterministic chain errors (for example, missing required active authority or RC/validation failures) will re-open the extension approval popup and run the same transaction again even though changing RPC nodes cannot fix it. This can also hide the original actionable error if the user cancels the second prompt. Gate this retry to node/transport outage signatures and otherwise throw the first failure immediately.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7452d35-5553-40de-98e6-72bf4e0cd716

📥 Commits

Reviewing files that changed from the base of the PR and between 9ff003a and 37e3f31.

⛔ Files ignored due to path filters (13)
  • packages/sdk/dist/browser/hive.js is excluded by !**/dist/**
  • packages/sdk/dist/browser/hive.js.map is excluded by !**/dist/**, !**/*.map
  • packages/sdk/dist/browser/index.d.ts is excluded by !**/dist/**
  • packages/sdk/dist/browser/index.js is excluded by !**/dist/**
  • packages/sdk/dist/browser/index.js.map is excluded by !**/dist/**, !**/*.map
  • packages/sdk/dist/node/hive.cjs is excluded by !**/dist/**
  • packages/sdk/dist/node/hive.cjs.map is excluded by !**/dist/**, !**/*.map
  • packages/sdk/dist/node/hive.mjs is excluded by !**/dist/**
  • packages/sdk/dist/node/hive.mjs.map is excluded by !**/dist/**, !**/*.map
  • packages/sdk/dist/node/index.cjs is excluded by !**/dist/**
  • packages/sdk/dist/node/index.cjs.map is excluded by !**/dist/**, !**/*.map
  • packages/sdk/dist/node/index.mjs is excluded by !**/dist/**
  • packages/sdk/dist/node/index.mjs.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (19)
  • apps/web/src/specs/utils/extension-error.spec.ts
  • apps/web/src/utils/extension-error.ts
  • packages/sdk/CHANGELOG.md
  • packages/sdk/package.json
  • packages/sdk/src/hive-tx/helpers/ByteBuffer.ts
  • packages/sdk/src/hive-tx/helpers/serializer.ts
  • packages/sdk/src/modules/accounts/utils/account-power.spec.ts
  • packages/sdk/src/modules/accounts/utils/parse-accounts.spec.ts
  • packages/sdk/src/modules/accounts/utils/profile-metadata.spec.ts
  • packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts
  • packages/sdk/src/modules/core/utils/parse-asset.spec.ts
  • packages/sdk/src/modules/posts/queries/get-account-posts-query-options.spec.ts
  • packages/sdk/src/modules/posts/queries/get-post-query-options.spec.ts
  • packages/sdk/src/modules/posts/queries/get-posts-ranked-query-options.spec.ts
  • packages/sdk/src/modules/posts/utils/filter-dmca-entries.spec.ts
  • packages/sdk/src/modules/posts/utils/filter-dmca-entries.ts
  • packages/sdk/src/modules/posts/utils/waves-helpers.spec.ts
  • packages/wallets/CHANGELOG.md
  • packages/wallets/package.json

📝 Walkthrough

Walkthrough

This PR adds normalized extension error utilities and tests, applies them across Keychain helpers and Hive extension broadcasts (including a one-shot fallback RPC retry), surfaces mutation errors in Promote, introduces invalidateAfterBroadcast, and switches many SDK mutation post-broadcast invalidations to use it while changing broadcastMode default to 'async'.

Changes

Keychain error handling and RPC failover

Layer / File(s) Summary
Types and error utilities
apps/web/src/types/keychain-impl.ts, apps/web/src/utils/extension-error.ts, apps/web/src/specs/utils/extension-error.spec.ts
Add message?: string and error?: unknown to TxResponse; implement extensionErrorMessage, isUserCancellation, and isRetryableNodeError; include Jest tests covering behaviors.
Keychain methods with standardized error messages
apps/web/src/utils/keychain.ts
Replace hardcoded "Operation cancelled" rejections in multiple Keychain helpers with extensionErrorMessage(resp, "Operation cancelled").
Hive extensions broadcast with RPC failover
apps/web/src/utils/hive-extensions.ts
Rewrite broadcast to promise-based attempt(node) with timeout; on non-cancellation and when rpc was not pinned, retry once via curated fallback RPC if classified retryable; final failures reject with extensionErrorMessage(...).
Promote component error state and display
apps/web/src/features/shared/promote/index.tsx
Destructure promoteError/resetPromote from mutation, reset prior error before submit, wrap submit in try/catch, and conditionally render promoteError.message when present and postError is absent.
Invalidate-after-broadcast and broadcast defaults
packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts, packages/sdk/src/modules/core/mutations/use-broadcast-mutation.ts, packages/sdk/src/modules/core/mutations/index.ts
Add invalidateAfterBroadcast (with BROADCAST_INCLUSION_DELAY_MS) and re-export it; mark 'sync' deprecated and change default broadcastMode to 'async' in useBroadcastMutation internals/docs.
SDK mutations: centralized post-broadcast invalidation
packages/sdk/src/modules/... (many account/wallet mutation files)
Update numerous mutation modules to call invalidateAfterBroadcast(auth?.adapter, broadcastMode, keys) instead of conditionally calling auth.adapter.invalidateQueries, preserving invalidation targets while centralizing timing/behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HiveExtensions
  participant KeychainRPC
  participant FallbackRPC
  Client->>HiveExtensions: broadcast(tx, rpc?)
  HiveExtensions->>KeychainRPC: attempt broadcast on primary
  alt success
    KeychainRPC-->>Client: return result
  else failure and rpc == null and not user-cancel and isRetryableNodeError
    HiveExtensions->>FallbackRPC: retry on fallback RPC
    alt success
      FallbackRPC-->>Client: return result
    else failure
      HiveExtensions-->>Client: reject(extensionErrorMessage)
    end
  else user cancellation or rpc pinned
    HiveExtensions-->>Client: reject(extensionErrorMessage)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Errors untangled, gentle and bright,

When broadcasts wobble, we try once more,
Messages clearer than cryptic lore,
Promote now tells what went wrong in sight,
A hopping cheer for stable flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main fix: improving Keychain broadcast error handling and adding fallback retry logic via a known-good RPC node.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/keychain-broadcast-error-and-fallback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/web/src/specs/utils/extension-error.spec.ts (1)

39-45: ⚡ Quick win

Add an object-shaped cancellation case to the positive matrix.

Please add a case like { error: { message: "user_cancel" } } so cancellation detection won’t regress for non-string extension payloads.

Suggested test addition
   it.each([
     [{ error: "user_cancel" }],
+    [{ error: { message: "user_cancel" } }],
     [{ message: "Request was canceled by the user." }],
     [{ message: "User declined the transaction" }],
   ])("treats %o as a cancellation", (resp) => {
     expect(isUserCancellation(resp)).toBe(true);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/specs/utils/extension-error.spec.ts` around lines 39 - 45, Add
an extra positive test case to the existing it.each matrix in
extension-error.spec.ts to cover object-shaped payloads so isUserCancellation
continues to detect cancellations for non-string extension payloads;
specifically, add a case like { error: { message: "user_cancel" } } to the array
passed to it.each so the test invoking isUserCancellation(resp) expects true for
that shape as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/utils/extension-error.ts`:
- Around line 47-53: isUserCancellation currently only checks resp.error when
it's a string, so object-shaped errors (e.g., { message: "user_cancel" } or
other serialized payloads) are missed; update isUserCancellation to derive a
normalized string from resp.error even when it's an object by checking
resp.error.message, resp.error.type, or JSON.stringify(resp.error) and
lowercasing that value (e.g., compute errVal from typeof resp.error === "string"
? resp.error : resp.error?.message ?? resp.error?.type ??
JSON.stringify(resp.error) ?? ""), then use errVal alongside resp.message to
detect "cancel"/"user_cancel"/"declined" occurrences; keep the existing boolean
checks but replace use of resp.error with the normalized errVal to ensure
object-shaped cancellation errors are recognized.

---

Nitpick comments:
In `@apps/web/src/specs/utils/extension-error.spec.ts`:
- Around line 39-45: Add an extra positive test case to the existing it.each
matrix in extension-error.spec.ts to cover object-shaped payloads so
isUserCancellation continues to detect cancellations for non-string extension
payloads; specifically, add a case like { error: { message: "user_cancel" } } to
the array passed to it.each so the test invoking isUserCancellation(resp)
expects true for that shape as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bf43cf7a-b797-4c9a-b494-886ca5e34091

📥 Commits

Reviewing files that changed from the base of the PR and between 27a924a and 6378c87.

📒 Files selected for processing (6)
  • apps/web/src/features/shared/promote/index.tsx
  • apps/web/src/specs/utils/extension-error.spec.ts
  • apps/web/src/types/keychain-impl.ts
  • apps/web/src/utils/extension-error.ts
  • apps/web/src/utils/hive-extensions.ts
  • apps/web/src/utils/keychain.ts

Comment thread apps/web/src/utils/extension-error.ts Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR fixes two layered issues in extension-based broadcast flows: the real extension error was previously discarded in favour of a hardcoded string, which also blocked the SDK's missing active authority auth-upgrade detection; and the Promote modal's mutateAsync lacked a try/catch, producing unhandled rejections. The fix surfaces real extension errors through a shared extensionErrorMessage helper, adds a targeted retry through a curated fallback node for genuine node/transport failures, and catches rejections in the modal.

  • extension-error.ts: new extensionErrorMessage, isUserCancellation, and isRetryableNodeError helpers with full unit-test coverage; isRetryableNodeError uses an explicit allowlist of connectivity patterns so deterministic chain errors (RC, missing authority, already-broadcasted) don't trigger a retry.
  • hive-extensions.ts / keychain.ts: all hardcoded "Extension broadcast failed" / "Operation cancelled" strings replaced with extensionErrorMessage(resp, <fallback>); retry in broadcastViaKeychain is gated on both !isUserCancellation and isRetryableNodeError.
  • SDK: default broadcastMode flipped from 'sync' to 'async'; new invalidateAfterBroadcast helper centralises query-invalidation timing (immediate for sync, ~4 s delay for async to avoid refetching pre-tx state).

Confidence Score: 5/5

Safe to merge. Error surfacing is additive, the retry is gated by two independent guards, and the promote modal now catches rejections instead of propagating them unhandled.

All issues from previous review threads appear addressed: isRetryableNodeError prevents retrying on RC/authority failures, the first-attempt error is re-thrown on resolved-failure retries, resetPromote() clears stale errors before each submission, and object-shaped cancellation codes are handled through normalizeErrorText. The two remaining suggestions are minor defensive hardening, not present defects on the happy or common-failure paths.

apps/web/src/utils/hive-extensions.ts — the retry rejection path (fallback node timeout) still propagates the fallback node's error instead of the first attempt's. packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts — the fire-and-forget setTimeout could silently swallow an invalidateQueries exception.

Important Files Changed

Filename Overview
apps/web/src/utils/extension-error.ts New utility: extensionErrorMessage, isUserCancellation, isRetryableNodeError. Object-shaped errors handled via normalizeErrorText; retryable list is conservative (allowlist of transport signals, excludes 500/deterministic errors).
apps/web/src/utils/hive-extensions.ts broadcastViaKeychain refactored to retry once via a fallback node, gated by isRetryableNodeError; first-attempt error surfaced on resolved-failure retries; rejected retry (timeout on fallback) still propagates the fallback error.
apps/web/src/features/shared/promote/index.tsx handleSubmit wraps preCheck+promote in try/catch; resetPromote() clears stale error before each attempt; promoteError rendered inline with postError guard.
packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts New helper centralises post-broadcast cache invalidation: sync mode awaits immediately, async mode defers via setTimeout(4 s). The async path returns void so callers awaiting it proceed immediately while invalidation fires later.
packages/sdk/src/modules/core/mutations/use-broadcast-mutation.ts Default broadcastMode flipped from 'sync' to 'async'. All callers without an explicit broadcastMode now get mempool-acceptance semantics instead of block-inclusion.
apps/web/src/specs/utils/extension-error.spec.ts Good coverage: extensionErrorMessage fallback/dedup, isUserCancellation for string and object-shaped errors (EIP-1193 code 4001), isRetryableNodeError allowlist vs. deterministic/cancel failures.
apps/web/src/utils/keychain.ts All hardcoded 'Operation cancelled' fallback strings replaced with extensionErrorMessage uniformly across 9 call sites.
apps/web/src/types/keychain-impl.ts TxResponse extended with optional message?: string and error?: unknown fields matching what Keychain-compatible extensions actually return.

Sequence Diagram

sequenceDiagram
    participant UI as Promote Modal
    participant EXT as Browser Extension
    participant NODE1 as User Keychain Node
    participant NODE2 as Fallback Node

    UI->>EXT: "requestBroadcast(ops, rpc=null)"
    EXT->>NODE1: broadcast_transaction

    alt Node outage / transport failure
        NODE1-->>EXT: 502 / ECONNREFUSED
        EXT-->>UI: "success=false, message=status code 502"
        Note over UI: isRetryableNodeError=true AND not cancel
        UI->>EXT: "requestBroadcast(ops, rpc=fallbackNode)"
        EXT->>NODE2: broadcast_transaction
        NODE2-->>EXT: ok
        EXT-->>UI: "success=true"
        UI->>UI: setStep(2)
    else User cancels
        EXT-->>UI: "success=false, error=user_cancel"
        Note over UI: isUserCancellation=true — no retry
        UI->>UI: promoteError shown inline
    else Deterministic chain error
        NODE1-->>EXT: missing required active authority
        EXT-->>UI: "success=false, message=missing required active authority"
        Note over UI: isRetryableNodeError=false — no retry
        UI->>UI: SDK auth-upgrade flow triggers
    end
Loading

Fix All in Claude Code

Reviews (5): Last reviewed commit: "chore: apply changeset versioning for PR..." | Re-trigger Greptile

Comment thread apps/web/src/utils/hive-extensions.ts
Comment thread apps/web/src/utils/hive-extensions.ts
Comment thread apps/web/src/features/shared/promote/index.tsx Outdated
Comment thread apps/web/src/utils/extension-error.ts
feruzm added 3 commits May 22, 2026 20:18
…iew)

Addresses PR review feedback:
- Only retry the broadcast through the fallback node on node/transport failures
  (new isRetryableNodeError) — not on deterministic chain errors (missing
  authority, RC, already-broadcast), which fail identically on any node and
  would needlessly re-open the extension popup.
- On a failed retry, surface the FIRST attempt's error (more semantically
  relevant — e.g. lets the SDK detect missing-authority) instead of the
  fallback node's error.
- isUserCancellation now handles object-shaped errors (e.g. { code: 4001,
  message: "User rejected request" }) and "reject"/"declined" wording.
- Promote modal: reset the mutation error before each attempt and don't render a
  stale promote error alongside a live preCheck error.
- Extend tests for object cancellations and isRetryableNodeError.
Switch the default BroadcastMode from 'sync' to 'async' so the SDK uses
broadcast_transaction instead of the deprecated broadcast_transaction_synchronous.
Transport/RPC errors are still thrown immediately; only the block-inclusion wait
is dropped. 'sync' remains available as an explicit opt-in and broadcastOperations
is marked @deprecated. No callers currently pass an explicit broadcastMode, and
block_num consumers already use optional chaining.
The sync→async default flip introduced a stale-read window: hooks invalidated
chain-derived queries (balances, account, follow state) synchronously in
onSuccess, but async broadcast resolves at mempool acceptance — before the tx is
in a block — so the refetch returned pre-transaction state.

- Add invalidateAfterBroadcast(adapter, mode, keys): refetch immediately for
  'sync', defer ~1 block (4s) for 'async'/default.
- Route all 28 balance/chain-state mutation hooks through it. This also fixes the
  3 hooks whose existing async-delay branched on the hook's local broadcastMode
  (undefined when omitted), bypassing the delay while the broadcast ran async.
- Fix use-claim-account invalidation key: ["accounts", creator] matched no query
  (account-full is keyed get-account-full); use QueryKeys.accounts.full(creator).
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts`:
- Around line 22-26: The exported function invalidateAfterBroadcast currently
types the keys parameter as any[][]; change this to a non-any shape such as
unknown[][] (or Array<Array<unknown>>) so the public API is strictly typed while
remaining compatible with PlatformAdapter.invalidateQueries; update the function
signature in invalidateAfterBroadcast(..., keys: unknown[][]) and ensure any
other references/overloads use the same unknown[][] type before forwarding keys
to adapter.invalidateQueries(keys).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0279ba46-a14e-4247-b735-45b16097b23c

📥 Commits

Reviewing files that changed from the base of the PR and between 6378c87 and 9ff003a.

📒 Files selected for processing (36)
  • apps/web/src/features/shared/promote/index.tsx
  • apps/web/src/specs/utils/extension-error.spec.ts
  • apps/web/src/utils/extension-error.ts
  • apps/web/src/utils/hive-extensions.ts
  • packages/sdk/src/modules/accounts/mutations/use-account-update.ts
  • packages/sdk/src/modules/accounts/mutations/use-claim-account.ts
  • packages/sdk/src/modules/accounts/mutations/use-create-account.ts
  • packages/sdk/src/modules/accounts/mutations/use-follow.ts
  • packages/sdk/src/modules/accounts/mutations/use-grant-posting-permission.ts
  • packages/sdk/src/modules/accounts/mutations/use-unfollow.ts
  • packages/sdk/src/modules/core/hive-tx.ts
  • packages/sdk/src/modules/core/mutations/index.ts
  • packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts
  • packages/sdk/src/modules/core/mutations/use-broadcast-mutation.ts
  • packages/sdk/src/modules/wallet/mutations/use-claim-engine-rewards.ts
  • packages/sdk/src/modules/wallet/mutations/use-claim-interest.ts
  • packages/sdk/src/modules/wallet/mutations/use-convert.ts
  • packages/sdk/src/modules/wallet/mutations/use-delegate-engine-token.ts
  • packages/sdk/src/modules/wallet/mutations/use-delegate-rc.ts
  • packages/sdk/src/modules/wallet/mutations/use-delegate-vesting-shares.ts
  • packages/sdk/src/modules/wallet/mutations/use-engine-market-order.ts
  • packages/sdk/src/modules/wallet/mutations/use-lock-larynx.ts
  • packages/sdk/src/modules/wallet/mutations/use-power-larynx.ts
  • packages/sdk/src/modules/wallet/mutations/use-set-withdraw-vesting-route.ts
  • packages/sdk/src/modules/wallet/mutations/use-stake-engine-token.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-engine-token.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-from-savings.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-larynx.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-point.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-spk.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-to-savings.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer-to-vesting.ts
  • packages/sdk/src/modules/wallet/mutations/use-transfer.ts
  • packages/sdk/src/modules/wallet/mutations/use-undelegate-engine-token.ts
  • packages/sdk/src/modules/wallet/mutations/use-unstake-engine-token.ts
  • packages/sdk/src/modules/wallet/mutations/use-withdraw-vesting.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/sdk/src/modules/core/hive-tx.ts

Comment on lines +22 to +26
export function invalidateAfterBroadcast(
adapter: PlatformAdapter | null | undefined,
broadcastMode: BroadcastMode | undefined,
keys: any[][]
): void | Promise<void> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate PlatformAdapter.invalidateQueries signature
rg -n --type=ts -C3 'interface\s+PlatformAdapter|type\s+PlatformAdapter|invalidateQueries\s*\('

# Locate invalidateAfterBroadcast signature/usages
rg -n --type=ts -C2 '\binvalidateAfterBroadcast\s*\('
rg -n --type=ts -C2 '\bkeys:\s*any\[\]\[\]'

Repository: ecency/vision-next

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the current signature in the reviewed file
sed -n '1,120p' packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts | nl -ba | sed -n '1,120p'

# 2) Show the relevant lines around keys type (if file is larger, this still should include it)
rg -n --no-heading 'export function invalidateAfterBroadcast|keys:' packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts -n -S

# 3) Print PlatformAdapter.invalidateQueries signature from source
rg -n --no-heading 'invalidateQueries\s*\(' packages/sdk/src/modules/core/types/platform-adapter.ts -n -S
sed -n '1,120p' packages/sdk/src/modules/core/types/platform-adapter.ts | nl -ba | sed -n '1,140p'

# 4) Find call sites of invalidateAfterBroadcast and inspect how keys are passed
rg -n --no-heading '\binvalidateAfterBroadcast\s*\(' packages/sdk/src -S

# 5) Find any usage that passes any[][]-shaped values (fallback: locate "keys" variable near calls)
rg -n --no-heading -C3 '\binvalidateAfterBroadcast\s*\(' packages/sdk/src | head -n 200

Repository: ecency/vision-next

Length of output: 105


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts"

# 1) Print the exact function signature block (around the export) with line numbers via awk
awk 'NR>=1 && NR<=120 {print NR ":" $0}' "$FILE" | rg -n 'invalidateAfterBroadcast|keys:' -n -S || true
# Also print a wider window around where the signature is
SIG_LINE=$(rg -n --no-heading 'export function invalidateAfterBroadcast' "$FILE" | cut -d: -f1 | head -n1 || true)
if [[ -n "${SIG_LINE:-}" ]]; then
  START=$((SIG_LINE-10)); END=$((SIG_LINE+20))
  awk -v s="$START" -v e="$END" 'NR>=s && NR<=e {print NR ":" $0}' "$FILE"
fi

# 2) Print the invalidateQueries type from PlatformAdapter
PA="packages/sdk/src/modules/core/types/platform-adapter.ts"
rg -n --no-heading 'invalidateQueries\s*\(' "$PA" -S
awk 'NR>=1 && NR<=120 {print NR ":" $0}' "$PA" | rg -n 'interface PlatformAdapter|invalidateQueries|queryKey' -n -S || true

# 3) Locate call sites of invalidateAfterBroadcast (inspect how keys are passed)
rg -n --no-heading '\binvalidateAfterBroadcast\s*\(' packages/sdk/src -S

# 4) For each call site, print the surrounding lines (small windows)
rg -n --no-heading '\binvalidateAfterBroadcast\s*\(' packages/sdk/src -S | cut -d: -f1 | head -n 20 | while read -r line; do
  # line numbers refer to file-local, so we need file too. We'll re-grep per file for that line.
  :
done
# Better approach: print small contexts directly around matches
rg -n -C4 --no-heading '\binvalidateAfterBroadcast\s*\(' packages/sdk/src -S | head -n 200

Repository: ecency/vision-next

Length of output: 1014


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show exact PlatformAdapter.invalidateQueries signature/type
PA="packages/sdk/src/modules/core/types/platform-adapter.ts"
rg -n --no-heading 'invalidateQueries\s*\(' "$PA" -S
awk 'NR>=1 && NR<=220 {print NR ":" $0}' "$PA" | rg -n 'invalidateQueries|queryKey|QueryKey|PartialMatchKey|prefix|interface PlatformAdapter' -n -S || true

# Show all call sites of invalidateAfterBroadcast (small windows)
rg -n -C4 --no-heading '\binvalidateAfterBroadcast\s*\(' packages/sdk/src -S
rg -n -C4 --no-heading '\binvalidateAfterBroadcast\s*\(' packages -S

Repository: ecency/vision-next

Length of output: 44


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show PlatformAdapter interface section including invalidateQueries type
PA="packages/sdk/src/modules/core/types/platform-adapter.ts"
IFACE_LINE=$(rg -n --no-heading 'interface PlatformAdapter' "$PA" | head -n1 | cut -d: -f1 || true)
if [[ -n "${IFACE_LINE:-}" ]]; then
  START=$((IFACE_LINE-10)); END=$((IFACE_LINE+120))
  awk -v s="$START" -v e="$END" 'NR>=s && NR<=e {print NR ":" $0}' "$PA"
fi

# 2) Locate any usages of invalidateAfterBroadcast
rg -n --no-heading '\binvalidateAfterBroadcast\s*\(' packages -S | head -n 50 || true

# 3) If there are usages, inspect those files' keys argument shapes
# (prints 8 lines before/after each match)
rg -n -C8 --no-heading '\binvalidateAfterBroadcast\s*\(' packages -S | head -n 200 || true

Repository: ecency/vision-next

Length of output: 28798


🏁 Script executed:

#!/bin/bash
set -euo pipefail

PA="packages/sdk/src/modules/core/types/platform-adapter.ts"

# Show the invalidateQueries member block (likely later in file)
line=$(rg -n --no-heading 'invalidateQueries\s*' "$PA" | head -n 1 | cut -d: -f1 || true)
if [[ -n "${line:-}" ]]; then
  START=$((line-20)); END=$((line+60))
  awk -v s="$START" -v e="$END" 'NR>=s && NR<=e {print NR ":" $0}' "$PA"
else
  echo "No invalidateQueries found in $PA"
fi

# Also show entire type of QueryKey if present (optional)
rg -n --no-heading 'QueryKey|PartialMatchKey|queryKey\s*:' "$PA" -S

Repository: ecency/vision-next

Length of output: 2904


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find adapter implementations / assignments of invalidateQueries
rg -n --no-heading 'invalidateQueries\s*:\s*\(' packages -S || true
rg -n --no-heading 'invalidateQueries\s*\?' packages/sdk/src -S || true
rg -n --no-heading '\binvalidateQueries\s*=' packages -S || true

# Also find any casts/usages of invalidateAfterBroadcast keys that might affect typing
rg -n --no-heading 'invalidateAfterBroadcast\s*\(' packages/sdk/src/modules -S

Repository: ecency/vision-next

Length of output: 4171


Remove explicit any from invalidateAfterBroadcast keys parameter

packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts exports invalidateAfterBroadcast(..., keys: any[][]) and forwards keys to adapter.invalidateQueries(keys). Replace any[][] with a non-any key shape (e.g., unknown[][] / Array<Array<unknown>>) so the helper’s public API is strictly typed (while still matching the current PlatformAdapter.invalidateQueries parameter type).

🧰 Tools
🪛 ESLint

[error] 25-25: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk/src/modules/core/mutations/invalidate-after-broadcast.ts` around
lines 22 - 26, The exported function invalidateAfterBroadcast currently types
the keys parameter as any[][]; change this to a non-any shape such as
unknown[][] (or Array<Array<unknown>>) so the public API is strictly typed while
remaining compatible with PlatformAdapter.invalidateQueries; update the function
signature in invalidateAfterBroadcast(..., keys: unknown[][]) and ensure any
other references/overloads use the same unknown[][] type before forwarding keys
to adapter.invalidateQueries(keys).

feruzm added 2 commits May 22, 2026 21:39
- isRetryableNodeError: drop the over-broad bare "network" and the 500/"internal
  server error" signals (they can wrap deterministic chain rejections); keep
  specific transport/gateway signatures (ECONN*, ETIMEDOUT, network error,
  failed to fetch, 502/503/504, bad gateway, origin servers unavailable, ...).
- invalidateAfterBroadcast: call adapter.invalidateQueries as a method instead of
  extracting it, so `this` stays bound.
- Tests for the tightened classification.
The SDK ships via tsup (no type-check), so type errors had accumulated. Fixed
all of them; 366 tests still pass.

Production (5): ByteBuffer Uint8Array->BufferSource casts; drop unused
Int8/Int32 serializers; precise non-null array result in filterDmcaEntry.

Tests (107): cast loosely-invoked queryFn in query-option specs; assert
possibly-undefined array/profile access in parse-accounts; SMTAsset.amount is a
string (matches the Hive API; parseAsset parseFloats it) so snapshots are
unchanged; loosen profile-metadata token literals; drop unused imports.
@feruzm feruzm added the patch Bug fixes and patches (1.0.0 → 1.0.1) label May 23, 2026
@feruzm feruzm merged commit 466a4ba into develop May 23, 2026
2 of 3 checks passed
@feruzm feruzm deleted the bugfix/keychain-broadcast-error-and-fallback branch May 23, 2026 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Bug fixes and patches (1.0.0 → 1.0.1)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant