-
-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem Statement
Context7 Benchmark Impact: Q1 scored 82/100, Q6 scored 87/100
Q1 Feedback (82/100):
"Absence of explicit error handling patterns during deserialization, no discussion of version compatibility, minimal coverage of performance implications for deeply nested structures"
Q6 Feedback (87/100):
"Absence of explicit error handling in most examples, minimal coverage of debugging techniques, lack of concrete examples showing how to handle deserialization failures"
Critical Gaps:
- No error handling patterns documentation
- No deserialization failure examples
- No debugging techniques guide
- Limited troubleshooting content
Proposed Solution
Add comprehensive error handling and debugging documentation.
1. New Page: docs/guide/error-handling.md
---
title: Error Handling
description: Patterns for handling errors in LUMOS-based Solana programs
---
# Error Handling
## Rust Error Handling
### Deserialization Errors
Handle Borsh deserialization failures:
```rust
use anchor_lang::prelude::*;
use borsh::BorshDeserialize;
pub fn process_account(data: &[u8]) -> Result<PlayerAccount> {
PlayerAccount::try_from_slice(data)
.map_err(|e| {
msg!("Failed to deserialize PlayerAccount: {}", e);
error!(ErrorCode::DeserializationFailed)
})
}
#[error_code]
pub enum ErrorCode {
#[msg("Failed to deserialize account data")]
DeserializationFailed,
}Account Discriminator Validation
Validate account type before deserializing:
use anchor_lang::Discriminator;
pub fn validate_and_deserialize(data: &[u8]) -> Result<PlayerAccount> {
// Check discriminator (first 8 bytes)
let disc = &data[..8];
if disc != PlayerAccount::DISCRIMINATOR {
return Err(error!(ErrorCode::InvalidAccountType));
}
PlayerAccount::try_from_slice(&data[8..])
.map_err(|_| error!(ErrorCode::DeserializationFailed))
}Field Validation
Validate field values after deserialization:
pub fn update_score(ctx: Context<UpdateScore>, amount: u64) -> Result<()> {
let player = &mut ctx.accounts.player;
// Validate before operation
require!(
amount > 0,
ErrorCode::InvalidAmount
);
// Check for overflow
let new_score = player.score
.checked_add(amount)
.ok_or(ErrorCode::ScoreOverflow)?;
require!(
new_score <= 1_000_000,
ErrorCode::ScoreExceedsMaximum
);
player.score = new_score;
Ok(())
}
#[error_code]
pub enum ErrorCode {
#[msg("Amount must be greater than zero")]
InvalidAmount,
#[msg("Score overflow detected")]
ScoreOverflow,
#[msg("Score exceeds maximum allowed value")]
ScoreExceedsMaximum,
}Type Validation
Ensure correct types during deserialization:
impl PlayerAccount {
pub fn validate(&self) -> Result<()> {
// Validate PublicKey is not default
require!(
self.wallet != Pubkey::default(),
ErrorCode::InvalidPublicKey
);
// Validate String is valid UTF-8
require!(
self.name.is_ascii(),
ErrorCode::InvalidName
);
// Validate Vec is within size limits
require!(
self.inventory.len() <= 100,
ErrorCode::InventoryTooLarge
);
Ok(())
}
}TypeScript Error Handling
Safe Deserialization
Wrap deserialization in try-catch:
import * as borsh from '@coral-xyz/borsh';
import { PlayerAccount, PlayerAccountSchema } from './generated';
function deserializePlayer(data: Buffer): PlayerAccount | null {
try {
// Skip 8-byte discriminator for Anchor accounts
const accountData = data.slice(8);
const player = borsh.deserialize(
PlayerAccountSchema,
accountData
) as PlayerAccount;
// Validate after deserialization
if (!validatePlayer(player)) {
console.error('Player validation failed');
return null;
}
return player;
} catch (error) {
console.error('Deserialization failed:', error);
return null;
}
}Validation Before Serialization
Validate data before sending to program:
function validatePlayer(player: PlayerAccount): boolean {
// Check required fields
if (!player.wallet || player.wallet.length !== 32) {
console.error('Invalid wallet address');
return false;
}
// Check number ranges
if (player.score < 0 || player.score > Number.MAX_SAFE_INTEGER) {
console.error('Score out of valid range');
return false;
}
// Check string constraints
if (!player.name || player.name.length > 32) {
console.error('Invalid name length');
return false;
}
return true;
}Type Guards for Enums
Safe enum handling with type guards:
type GameState =
| { kind: 'Active'; data: ActiveData }
| { kind: 'Paused'; data: PausedData }
| { kind: 'Finished'; score: number };
function isActiveState(
state: GameState
): state is Extract<GameState, { kind: 'Active' }> {
return state.kind === 'Active';
}
function handleGameState(state: GameState) {
if (isActiveState(state)) {
// TypeScript knows state.data is ActiveData
console.log('Game is active:', state.data);
} else if (state.kind === 'Paused') {
console.log('Game is paused:', state.data);
} else {
console.log('Game finished with score:', state.score);
}
}Handling RPC Errors
Graceful error handling for RPC calls:
async function fetchPlayerSafe(
connection: Connection,
address: PublicKey
): Promise<PlayerAccount | null> {
try {
const accountInfo = await connection.getAccountInfo(address);
if (!accountInfo) {
console.log('Account not found');
return null;
}
if (accountInfo.data.length < 8) {
console.error('Account data too short');
return null;
}
return deserializePlayer(accountInfo.data);
} catch (error) {
if (error instanceof Error) {
console.error('RPC error:', error.message);
}
return null;
}
}Common Errors
Error: "Failed to deserialize account data"
Cause: Data format doesn't match schema
Solutions:
- Verify account discriminator matches
- Ensure schema matches on-chain data
- Check if account was migrated
Error: "Number precision lost"
Cause: u64/u128 exceeds JavaScript Number range
Solution: Use BigInt for large numbers
const balance = BigInt(accountData.balance);Error: "Invalid PublicKey"
Cause: Invalid base58 string or wrong length
Solution: Validate before constructing
function safePublicKey(address: string): PublicKey | null {
try {
return new PublicKey(address);
} catch {
return null;
}
}Error: "Circular dependency detected"
Cause: Type A references B, B references A
Solution: Restructure types or use references
// ❌ Circular
struct A { b: B }
struct B { a: A }
// ✅ Use Pubkey reference
struct A { b_address: Pubkey }
struct B { a_address: Pubkey }
### 2. New Page: `docs/guide/debugging.md`
```markdown
---
title: Debugging
description: Techniques for debugging LUMOS schemas and generated code
---
# Debugging LUMOS
## Schema Validation
### Check Schema Syntax
```bash
# Validate schema without generating code
lumos validate schemas/player.lumos
Common syntax errors:
- Missing colons after field names
- Undefined type references
- Invalid attribute syntax
- Circular dependencies
View Generated Code
# Generate and inspect
lumos generate schemas/player.lumos
# View Rust output
cat generated.rs
# View TypeScript output
cat generated.tsDeserialization Debugging
Rust: Print Binary Data
use hex;
pub fn debug_account_data(data: &[u8]) {
msg!("Account data (hex): {}", hex::encode(data));
msg!("Account data length: {}", data.len());
msg!("Discriminator: {}", hex::encode(&data[..8]));
}TypeScript: Inspect Binary Data
function debugAccountData(data: Buffer) {
console.log('Length:', data.length);
console.log('Hex:', data.toString('hex'));
console.log('Discriminator:', data.slice(0, 8).toString('hex'));
}Compare Expected vs Actual
#[cfg(test)]
mod tests {
#[test]
fn test_account_size() {
let expected_size = 8 + 32 + 2 + 8; // discriminator + wallet + level + score
let actual = PlayerAccount {
wallet: Pubkey::default(),
level: 1,
score: 0,
};
let serialized = actual.try_to_vec().unwrap();
assert_eq!(serialized.len(), expected_size - 8); // Exclude discriminator
}
}Performance Debugging
Measure Serialization Time
use std::time::Instant;
let start = Instant::now();
let bytes = account.try_to_vec()?;
msg!("Serialization took: {:?}", start.elapsed());Account Size Analysis
# Calculate account size
lumos size schemas/player.lumos --type PlayerAccountDeep Nesting Analysis
// Check nesting depth
fn calculate_nesting_depth<T>() -> usize {
// Implement recursive depth calculation
}
msg!("Nesting depth: {}", calculate_nesting_depth::<MyStruct>());Common Issues
Issue: Generated code doesn't compile
Debug steps:
- Check LUMOS CLI version:
lumos --version - Validate schema:
lumos validate schema.lumos - Check Rust/TS dependencies
- Review error messages carefully
Issue: Deserialization fails in tests
Debug steps:
- Print hex data before deserialization
- Compare with expected format
- Check discriminator matches
- Verify Borsh version consistency
Issue: Type mismatch between Rust and TypeScript
Debug steps:
- Regenerate both files from same schema
- Check type mappings are correct
- Verify Borsh schema definitions match
- Test serialization round-trip
### 3. New Page: `docs/reference/common-errors.md`
```markdown
---
title: Common Errors
description: Reference guide for common LUMOS errors and solutions
---
# Common Errors Reference
## CLI Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `Failed to parse schema at line X` | Syntax error | Check .lumos file syntax |
| `Undefined type: TypeName` | Type not defined | Define type or import |
| `Circular dependency detected` | Type A → B → A | Restructure types |
| `Invalid attribute: #[unknown]` | Unsupported attribute | Check documentation |
## Runtime Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `Deserialization failed` | Data format mismatch | Verify schema matches data |
| `Invalid discriminator` | Wrong account type | Check account address |
| `Number precision lost` | u64 > MAX_SAFE_INTEGER | Use BigInt |
| `Invalid PublicKey` | Bad base58 string | Validate format |
## Migration Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `Account size mismatch` | Schema size changed | Realloc before migration |
| `Version mismatch` | Old client, new data | Update client code |
| `Field not found` | Breaking change | Implement migration |
4. Update Existing Pages
Add "Error Handling" sections to:
docs/guide/getting-started.mddocs/guide/rust-integration.mddocs/guide/typescript-integration.md
5. Update Navigation
sidebar: {
'/guide/': [
// ... existing
{
text: 'Advanced',
items: [
{ text: 'Error Handling', link: '/guide/error-handling' },
{ text: 'Debugging', link: '/guide/debugging' },
]
}
],
'/reference/': [
// ... existing
{ text: 'Common Errors', link: '/reference/common-errors' },
]
}Acceptance Criteria
-
docs/guide/error-handling.mdcreated with comprehensive patterns -
docs/guide/debugging.mdcreated with debugging techniques -
docs/reference/common-errors.mdcreated with error reference table - Error handling sections added to 3 existing guide pages
- Navigation updated in
.vitepress/config.ts - Code examples include error handling
- Target: Q1: 82→92 (+10), Q6: 87→95 (+8)
Impact
Context7 Benchmark:
- Q1: 82 → 92 (+10 points)
- Q6: 87 → 95 (+8 points)
Overall Score: 88.0 → 89.8 (+1.8 points)
User Value:
- Reduced production errors
- Better debugging workflows
- Comprehensive error reference
- Clearer troubleshooting paths
Related
- Context7 Benchmark Questions 1, 6
- Rust error handling best practices
- TypeScript type safety patterns
Priority Justification
🟡 HIGH - Affects 2 questions, critical for production usage, improves developer experience