Skip to content

feat(errors): implement StellarError (#35) - #54

Merged
codebestia merged 1 commit into
ShadeProtocol:mainfrom
daveades:feat/35-stellar-error
Jul 29, 2026
Merged

feat(errors): implement StellarError (#35)#54
codebestia merged 1 commit into
ShadeProtocol:mainfrom
daveades:feat/35-stellar-error

Conversation

@daveades

@daveades daveades commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Description

Implements StellarError, the last exception in the SDK's typed hierarchy. It wraps the underlying stellar_sdk exception so callers keep access to the raw error while still catching a single SDK type, and exposes Horizon's result codes for programmatic handling.

StellarError(ShadeError) in src/shade/errors.py:

Attribute Contents
stellar_result_code Transaction-level Horizon code (tx_failed, tx_insufficient_fee, …). Falls back to the first failing operation code when Horizon reports no transaction code.
operation_result_codes Per-operation codes in Horizon's order, op_success entries included.
failed_operation_code Property returning the first non-op_success code, so callers can branch on op_no_trust / op_underfunded without walking the list.
original_error The raw stellar_sdk exception.
status_code / response_body Horizon HTTP status and raw body, when the failure came from Horizon.

Result codes are read from extras.result_codes, matching stellar_sdk 13.2.1's BaseHorizonError. Messages are built from two lookup tables (transaction + operation codes), preferring the operation-level failure since that is what actually went wrong:

Stellar transaction failed: the destination account has no trustline for the asset
(op_no_trust) (result code: tx_failed) (status code: 400)

Unrecognised codes are passed through raw rather than swallowed. Non-Horizon failures fall back to account_id (for AccountNotFoundException), then Horizon's title/detail, then str(exc).

wrap_stellar_errors() — a context manager covering the issue's third step. The Stellar integration layer wraps its Horizon/Soroban calls with it so callers only ever have to catch ShadeError:

with wrap_stellar_errors("Failed to submit payout txn_123"):
    server.submit_transaction(transaction)

Both are exported from the top-level shade package.

Fixes #35

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

27 new tests in tests/test_stellar_error.py, built against real stellar_sdk exception instances rather than mocks.

  • Rejected transactions: transaction and operation result codes, failed_operation_code skipping op_success, Horizon status/body capture, __str__ formatting, fallback to the operation code when no transaction code is present
  • Descriptive messages: missing trustline (op_no_trust, op_src_no_trust), underfunded accounts, operation code taking precedence over the transaction code, unrecognised codes passed through raw, explicit message override, title/detail fallback, AccountNotFoundException, non-Horizon SdkError
  • original_error exposes the raw exception and is None when constructed directly
  • Malformed Horizon payloads: non-dict result_codes, non-list operations, non-string entries — all degrade to "no result codes" instead of raising while building the exception
  • wrap_stellar_errors: converts and chains (__cause__), honours the message override, lets non-stellar_sdk exceptions through untouched, transparent on success

Full suite: 277 passed, 3 skipped, no regressions. Both CI flake8 gates clean (--select=E9,F63,F7,F82 and --max-complexity=10 --max-line-length=127).

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Note on scope

The issue's third proposed step says to catch stellar_sdk exceptions "in the Stellar integration layer". That layer (shade/stellar/, FEATURES.md §6) does not exist yet and has no issues filed for it. Rather than build it here, this PR delivers the catching mechanism as wrap_stellar_errors() so that work can simply apply it at each Horizon/Soroban call site. StellarError itself is complete and all three acceptance criteria are met.

Summary by CodeRabbit

  • New Features
    • Added clearer Stellar transaction and operation error reporting, including result codes and Horizon response details.
    • Added a context manager for converting Stellar SDK errors into actionable StellarError exceptions.
    • Exposed the new error handling tools through the package’s top-level API.
  • Bug Fixes
    • Improved handling of malformed or incomplete error responses without causing additional failures.
    • Preserved non-Stellar exceptions and successful operations unchanged.

Implements issue ShadeProtocol#35. StellarError wraps the underlying stellar_sdk
exception so callers keep the raw error while catching a single SDK type,
and exposes the Horizon result codes for programmatic handling.

- stellar_result_code carries the transaction-level code, falling back to
  the first failing operation code when Horizon reports no transaction one
- operation_result_codes keeps the per-operation codes in Horizon's order,
  with failed_operation_code skipping op_success entries
- original_error holds the raw stellar_sdk exception; Horizon status and
  response body are attached where available
- Messages describe the specific failure (missing trustline, underfunded
  account, ...) via result-code tables, falling back to the raw code so an
  unrecognised code still reaches the caller
- wrap_stellar_errors() context manager for the Stellar integration layer
  to catch and re-raise stellar_sdk failures

Malformed Horizon payloads degrade to "no result codes" rather than
raising while building the exception.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 845d4f88-3872-4df1-9ccf-4969e6388847

📥 Commits

Reviewing files that changed from the base of the PR and between c6a28ab and 8566421.

📒 Files selected for processing (3)
  • src/shade/__init__.py
  • src/shade/errors.py
  • tests/test_stellar_error.py

📝 Walkthrough

Walkthrough

The PR adds Horizon result-code parsing, a StellarError exception with original-error access and descriptive messages, an SdkError wrapping context manager, top-level package exports, and comprehensive tests.

Changes

Stellar error handling

Layer / File(s) Summary
Result code parsing and descriptions
src/shade/errors.py
Adds transaction and operation result-code descriptions plus defensive parsing of Horizon payloads and context.
StellarError construction and messages
src/shade/errors.py
Adds structured error fields, failed-operation selection, Horizon context, exception conversion, and derived failure messages.
Exception wrapper and public API
src/shade/errors.py, src/shade/__init__.py, tests/test_stellar_error.py
Wraps SdkError as StellarError, exports the new symbols, and tests conversion, formatting, malformed payloads, chaining, passthrough, and exports.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Horizon
  participant stellar_sdk
  participant wrap_stellar_errors
  participant StellarError
  Horizon-->>stellar_sdk: Return error body and result codes
  stellar_sdk-->>wrap_stellar_errors: Raise SdkError
  wrap_stellar_errors->>StellarError: Convert via from_exception
  StellarError-->>wrap_stellar_errors: Return structured exception
  wrap_stellar_errors-->>stellar_sdk: Raise StellarError with cause
Loading

Suggested reviewers: bukkybyte, kodesage

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: implementing StellarError.
Description check ✅ Passed The description covers the summary, issue reference, change type, testing, and checklist, with only optional dependency details omitted.
Linked Issues check ✅ Passed The PR satisfies #35 by adding StellarError, result codes, original_error, descriptive messages, and a wrapper for stellar_sdk failures.
Out of Scope Changes check ✅ Passed The changes stay focused on StellarError, wrapping, exports, and tests, with no unrelated functionality introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codebestia codebestia left a comment

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.

LGTM!
Thank you for your contribution

@codebestia
codebestia merged commit ff36ffa into ShadeProtocol:main Jul 29, 2026
2 checks passed
@grantfox-oss grantfox-oss Bot mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement StellarError

2 participants