-
Notifications
You must be signed in to change notification settings - Fork 2
fix(node): handle skipped Solana slots as unavailable blocks #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
OkekeChristian24
wants to merge
1
commit into
subquery:main
Choose a base branch
from
OkekeChristian24:fix-skipped-slots
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright 2020-2025 SubQuery Pte Ltd authors & contributors | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
|
|
||
| import { BlockUnavailableError } from '@subql/node-core'; | ||
| import { SolanaApi } from './api.solana'; | ||
|
|
||
| describe('SolanaApi errors', () => { | ||
| it('normalises skipped slots to BlockUnavailableError', () => { | ||
| const error = new Error( | ||
| 'Slot 394555688 was skipped, or missing due to ledger jump to recent snapshot', | ||
| ); | ||
|
|
||
| expect(SolanaApi.prototype.handleError.call({}, error)).toBeInstanceOf( | ||
| BlockUnavailableError, | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // Copyright 2020-2025 SubQuery Pte Ltd authors & contributors | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
|
|
||
| import { solanaBlockToHeader, transformBlock } from './block.solana'; | ||
| import { SolanaDecoder } from './decoder'; | ||
|
|
||
| const mockBlock = { | ||
| blockHeight: 10n, | ||
| blockTime: 1_744_000_000n, | ||
| blockhash: 'block-hash', | ||
| parentSlot: 12n, | ||
| previousBlockhash: 'parent-hash', | ||
| transactions: [], | ||
| } as any; | ||
|
|
||
| describe('Solana block utils', () => { | ||
| it('uses the fetched slot as the header height', () => { | ||
| expect(solanaBlockToHeader(mockBlock, 15).blockHeight).toBe(15); | ||
| }); | ||
|
|
||
| it('preserves the fetched slot through transformed blocks', () => { | ||
| const block = transformBlock(mockBlock, new SolanaDecoder(), 15); | ||
|
|
||
| expect(solanaBlockToHeader(block).blockHeight).toBe(15); | ||
| expect(Object.getOwnPropertyNames(block)).not.toContain('slot'); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: subquery/subql-solana
Length of output: 9473
🏁 Script executed:
Repository: subquery/subql-solana
Length of output: 6624
🌐 Web query:
@solana/kit SolanaError isSolanaError skip slot error code message stripped __DEV__💡 Result:
In the context of the
@solana/kitand@solana/errorslibraries, the SolanaError class and the isSolanaError type guard are used to handle typed errors [1][2][3]. When interacting with the Solana JSON-RPC, you may encounter server errors related to skipped or missing slots, such as: SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED [4] SOLANA_ERRORJSON_RPCSERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED [4] These errors often map to underlying JSON-RPC responses (like codes -32007 or -32009) [5][6][7] which indicate that a block was either explicitly skipped by the network due to leader/validator lag or is unavailable due to ledger data gaps [6]. Regarding your specific query components: 1. isSolanaError: This is a type guard function used to check if a caught error is a SolanaError and, optionally, to assert its specific error code [2][3]. When used with a code, it refines the TypeScript type of the error, allowing you to safely access the error's context (e.g., e.context) [2][8]. 2. SolanaError: A class that encapsulates an error's message, stack trace, and a specific numeric Solana error code [1][9]. 3. Slot skipped errors: These indicate that the requested slot does not contain block data, either because it was skipped during consensus or the data is missing from the node's local storage [6]. 4. DEV: In development environments, the@solana/errorslibrary may include additional error context or more descriptive messages that are sometimes stripped or minimized in production builds to reduce bundle size. To handle these errors, you should use the type guard to check for specific error codes rather than string-matching the raw JSON-RPC error message [10][11]: import { isSolanaError, SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED } from '@solana/kit'; try { //... rpc call } catch (e) { if (isSolanaError(e, SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED)) { // Handle skipped slot specifically using e.context.serverMessage } }Citations:
🌐 Web query:
site:docs.solana.com@solana/kitskipped slot error code isSolanaError💡 Result:
There is no publicly documented error code specifically named skipped slot error code or isSolanaError associated with a package named
@solana/kitin the provided technical documentation. It is possible that the package name or error nomenclature is non-standard, unofficial, or refers to a internal/deprecated library. In Solana development, the term skipped slot refers to a slot in the blockchain where no block was produced, which is a normal occurrence in the cluster [1]. Developers often encounter slot-related concepts when using commitment levels (such as processed, where a block may still be skipped) or via WebSocket subscriptions like slotSubscribe or slotsUpdatesSubscribe [1][2]. If you are encountering an error with a specific library, ensure you are using the official Solana Web3.js SDK or standard RPC methods, as these are the primary interfaces for interacting with the Solana network [1][2]. If isSolanaError is part of a custom error-handling utility in your project, it is likely a helper function designed to identify error types conforming to a specific Solana error interface.Citations:
🌐 Web query:
@solana/kit SolanaError isSolanaError skipped slot error code message stripped __DEV__💡 Result:
In the
@solana/kitecosystem, SolanaError is a structured error class used to standardize error handling across the library [1][2]. Key components and usage: 1. Error Codes and Identification: Solana errors are identified by specific, unique error codes (e.g., SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED) [3][1]. You should never rely on string matching to identify errors; instead, use the isSolanaError helper function, which acts as a TypeScript type guard [4][5]. 2. Handling Skipped Slots: A "skipped slot" occurs when a slot does not produce a block, typically because the leader was offline or the fork was abandoned [6]. When this happens, an RPC node may return a JSON-RPC error. - In@solana/kit, this is represented by the error code SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED [3]. - This error code corresponds to the underlying JSON-RPC error code -32007 [7][8]. - When using isSolanaError(error, SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED), the error context provides access to the serverMessage returned by the node [3]. 3. DEV and Message Stripping: In production-optimized builds, error messages are often "stripped" or replaced with generic placeholders to reduce bundle size and protect against information leakage [3][2]. The DEV environment variable is typically used by library internals to determine whether to include detailed, developer-friendly error messages or minimal, production-safe messages in the SolanaError object [2]. Example pattern for catching these errors: import { isSolanaError, SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED } from '@solana/kit'; try { //... operation that might trigger a slot error } catch (e) { if (isSolanaError(e, SOLANA_ERRORJSON_RPCSERVER_ERROR_SLOT_SKIPPED)) { console.error('The requested slot was skipped:', e.context.serverMessage); } else { throw e; } }Citations:
🏁 Script executed:
Repository: subquery/subql-solana
Length of output: 3158
Use Solana’s typed skipped-slot error instead of matching the message string.
e.message?.includes('was skipped')is brittle;@solana/kit/@solana/errorsalready exposeisSolanaError(...)and a dedicated skipped-slot code, so this should use the structured error path to avoid a silent regression if the text changes or is stripped.🤖 Prompt for AI Agents