solana_kit_errors

pub package docs website CI coverage

Structured errors for the Solana Kit SDK. Every failure in the SDK is a SolanaError carrying a numeric code from SolanaErrorCode and an optional context map, so callers can route on codes instead of parsing message strings.

This is the Dart port of @solana/errors from the Solana TypeScript SDK.

Installation

Install the package directly:

dependencies:
  "solana_kit_errors": ^0.9.3

If your app uses several Solana Kit packages together, you can also depend on the umbrella package instead:

dart pub add solana_kit

Inside this monorepo, Dart workspace resolution uses the local package automatically.

Documentation

For architecture notes, getting-started guides, and cross-package examples, start with the workspace docs site and then drill down into the package README and API reference.

Usage

Creating errors

SolanaError implements Exception, so it can be thrown and caught like any Dart exception. The context map is made unmodifiable at construction time.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  // Simple error with no context.
  final error = SolanaError(SolanaErrorCode.blockHeightExceeded);
  print(error);

  // Error with context variables interpolated into the message.
  final notFoundError = SolanaError(
    SolanaErrorCode.accountsAccountNotFound,
    {'address': '11111111111111111111111111111111'},
  );
  print(notFoundError);

  try {
    throw SolanaError(SolanaErrorCode.transactionFeePayerMissing);
  } on SolanaError catch (e) {
    print('Code: ${e.code}');
    print('Context: ${e.context}');
    print('Message: $e');
  }
}

Checking errors with isSolanaError

isSolanaError is a type guard. It checks whether a value is a SolanaError and optionally whether it matches a specific error code.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void handleError(Object? error) {
  // Check if it is any SolanaError.
  if (isSolanaError(error)) {
    print('Got a Solana error with code: ${(error as SolanaError).code}');
  }

  // Check for a specific error code.
  if (isSolanaError(error, SolanaErrorCode.transactionFeePayerMissing)) {
    print('Transaction is missing a fee payer!');
  }

  // Returns false for non-SolanaError values.
  print(isSolanaError('not an error')); // false
  print(isSolanaError(null)); // false
}

void main() {
  handleError(SolanaError(SolanaErrorCode.transactionFeePayerMissing));
}

Error message interpolation

Error messages are templates with $variable placeholders filled from the context map. getErrorMessage performs the interpolation.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  // Get an interpolated message directly.
  final message = getErrorMessage(
    SolanaErrorCode.addressesInvalidByteLength,
    {'actualLength': 28},
  );
  print(message);

  // Missing context values leave the placeholder as-is.
  final partial = getErrorMessage(SolanaErrorCode.addressesInvalidByteLength);
  print(partial);

  // Codes without a message template produce a fallback message.
  final unknown = getErrorMessage(SolanaErrorCode.blockHeightExceeded);
  print(unknown);
}

Converting JSON-RPC errors

getSolanaErrorFromJsonRpcError converts a JSON-RPC error response into a SolanaError. Preflight failures automatically extract the nested transaction error.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  // Typical JSON-RPC error response from a Solana node.
  final rpcError = <String, Object?>{
    'code': -32005,
    'message': 'Node is unhealthy',
    'data': <String, Object?>{},
  };

  final solanaError = getSolanaErrorFromJsonRpcError(rpcError);
  print(solanaError.code == SolanaErrorCode.jsonRpcServerErrorNodeUnhealthy);

  // Preflight failure errors automatically extract the nested transaction error.
  final preflightError = <String, Object?>{
    'code': -32002,
    'message': 'Transaction simulation failed',
    'data': <String, Object?>{
      'err': 'BlockhashNotFound',
      'logs': <String>[],
    },
  };

  final preflightSolanaError = getSolanaErrorFromJsonRpcError(preflightError);
  print(
    preflightSolanaError.code ==
        SolanaErrorCode.jsonRpcServerErrorSendTransactionPreflightFailure,
  );

  // The nested cause is available in the context.
  final cause = preflightSolanaError.context['cause'] as SolanaError;
  print(cause.code == SolanaErrorCode.transactionErrorBlockhashNotFound);

  // Malformed responses produce a malformedJsonRpcError.
  final malformed = getSolanaErrorFromJsonRpcError({'unexpected': 'data'});
  print(malformed.code == SolanaErrorCode.malformedJsonRpcError);
}

Converting transaction and instruction errors

Transaction errors from RPC responses use a Rust enum-like format. getSolanaErrorFromTransactionError handles both string and map forms, and delegates InstructionError entries to getSolanaErrorFromInstructionError.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  // Simple string error.
  final error = getSolanaErrorFromTransactionError('BlockhashNotFound');
  print(error.code == SolanaErrorCode.transactionErrorBlockhashNotFound);

  // Error with nested context.
  final rentError = getSolanaErrorFromTransactionError({
    'InsufficientFundsForRent': {'account_index': 2},
  });
  print(rentError.code == SolanaErrorCode.transactionErrorInsufficientFundsForRent);
  print(rentError.context['accountIndex']); // 2

  // Instruction errors nested in transaction errors are delegated.
  final txError = getSolanaErrorFromTransactionError({
    'InstructionError': [0, 'InvalidAccountData'],
  });
  print(txError.code == SolanaErrorCode.instructionErrorInvalidAccountData);

  // Custom program error with an error code.
  final customError = getSolanaErrorFromInstructionError(1, {'Custom': 42});
  print(customError.code == SolanaErrorCode.instructionErrorCustom);
  print(customError.context['code']); // 42
  print(customError.context['index']); // 1
}

Unwrapping simulation errors

When a transaction simulation fails, the actual error is wrapped in a simulation error. unwrapSimulationError gets at the underlying cause.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  final simulationError = SolanaError(
    SolanaErrorCode.jsonRpcServerErrorSendTransactionPreflightFailure,
    {
      'cause': SolanaError(SolanaErrorCode.transactionErrorBlockhashNotFound),
      'logs': <String>[],
    },
  );

  final underlying = unwrapSimulationError(simulationError);
  print(underlying is SolanaError);
  print(
    (underlying! as SolanaError).code ==
        SolanaErrorCode.transactionErrorBlockhashNotFound,
  );

  // Non-simulation errors are returned as-is.
  final regularError = SolanaError(SolanaErrorCode.blockHeightExceeded);
  print(identical(unwrapSimulationError(regularError), regularError));
}

Context encoding and decoding

encodeContextObject and decodeEncodedContext serialize error context maps to and from compact base64 strings, useful for transmitting error details.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  final context = {
    'address': '11111111111111111111111111111111',
    'index': 0,
  };

  // Encode to a compact base64 string.
  final encoded = encodeContextObject(context);
  print(encoded);

  // Decode back to the original map.
  final decoded = decodeEncodedContext(encoded);
  print(decoded['address']);
}

Typed Error Domains

solana_kit_errors includes domain helpers layered over numeric error codes. Use them to route error handling without hardcoding code ranges throughout your application.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void handleSolanaFailure(SolanaError error) {
  if (error.isInDomain(SolanaErrorDomain.rpc)) {
    print('RPC failure: $error');
    return;
  }

  if (error.isInDomain(SolanaErrorDomain.transaction)) {
    print('Transaction failure: $error');
    return;
  }

  print('Unhandled Solana error: $error');
}

This keeps your error-routing logic readable while still preserving the exact numeric code and context payload when you need lower-level diagnostics.

Preferred construction helpers

Use createSolanaError(...) and wrapSolanaError(...) when you want consistent null stripping, shared context keys, and nested-cause preservation.

import 'package:solana_kit_errors/solana_kit_errors.dart';

void main() {
  final error = wrapSolanaError(
    SolanaErrorCode.accountsFailedToDecodeAccount,
    StateError('decoder failed'),
    context: {
      SolanaErrorContextKeys.address: '11111111111111111111111111111111',
      SolanaErrorContextKeys.operation: 'decodeAccount',
    },
  );

  print(error.context[SolanaErrorContextKeys.causeType]); // StateError
}

Prefer shared keys such as address, operation, methodName, path, statusCode, and url so diagnostics stay predictable across packages.

API Reference

Classes

  • SolanaError: core error class implementing Exception. Carries an int code and an unmodifiable Map<String, Object?> context.
  • SolanaErrorCode: abstract final class with 100+ static const int error codes grouped by category (general, JSON-RPC, addresses, accounts, keys, instructions, instruction errors, signers, transactions, transaction errors, codecs, RPC, RPC subscriptions, program clients, invariant violations).
  • RpcEnumErrorConfig: configuration class for mapping Solana RPC enum-style errors to SolanaError instances.
  • SolanaErrorContextKeys: shared key names for structured diagnostics such as address, operation, methodName, path, statusCode, and nested cause fields.
  • SolanaErrorDomain: enum describing high-level error domains (for example rpc, transaction, codecs, mobileWalletAdapter).

Functions

  • isSolanaError(Object? e, [int? code]): type guard that checks whether a value is a SolanaError, optionally matching a specific code.
  • getSolanaErrorDomain(int code): classifies numeric error codes into typed SolanaErrorDomain values.
  • isSolanaErrorCodeInDomain(int code, SolanaErrorDomain domain): checks if a numeric code belongs to a domain.
  • isSolanaErrorInDomain(Object? error, SolanaErrorDomain domain): checks if an Object? is a SolanaError in a domain.
  • getErrorMessage(int code, [Map<String, Object?> context]): returns the interpolated error message for a given error code and context.
  • createSolanaError(int, {Map<String, Object?> context, Object? cause}): creates a SolanaError with normalized context and optional nested cause details.
  • wrapSolanaError(int, Object, {Map<String, Object?> context}): wraps an existing exception or SolanaError while preserving structured cause information.
  • createSolanaErrorContext(Map<String, Object?>, {Object? cause}): normalizes context maps by dropping nulls and attaching consistent nested-cause metadata.
  • getSolanaErrorFromJsonRpcError(Object?): converts a JSON-RPC error response map into a SolanaError.
  • getSolanaErrorFromTransactionError(Object): converts a Solana RPC transaction error into a SolanaError.
  • getSolanaErrorFromInstructionError(num index, Object): converts a Solana RPC instruction error into a SolanaError.
  • getSolanaErrorFromRpcError(RpcEnumErrorConfig, Object): low-level converter for RPC enum-style errors.
  • unwrapSimulationError(Object?): extracts the underlying cause from simulation-related errors.
  • encodeContextObject(Map<String, Object?>): encodes a context map to a compact base64 string.
  • decodeEncodedContext(String): decodes a base64-encoded context string back into a map.

Constants

  • solanaErrorMessages: Map<int, String> mapping every SolanaErrorCode to its human-readable message template.

Example

Use example/main.dart as a runnable starting point for solana_kit_errors.

  • Import path: package:solana_kit_errors/solana_kit_errors.dart
  • This section is centrally maintained with mdt to keep package guidance aligned.
  • After updating shared docs templates, run docs:update from the repo root.

Maintenance

  • Validate docs in CI and locally with docs:check.
  • Keep examples focused on one workflow and reference package README sections for deeper API details.

Libraries

solana_kit_errors
Structured error types and error-code utilities for the Solana Kit Dart SDK.