Skip to content

fix(orders): preserve venue error codes for passive submissions - #91

Merged
outerlook merged 2 commits into
developfrom
fix/passive-error-taxonomy
Jul 30, 2026
Merged

fix(orders): preserve venue error codes for passive submissions#91
outerlook merged 2 commits into
developfrom
fix/passive-error-taxonomy

Conversation

@outerlook

@outerlook outerlook commented Jul 30, 2026

Copy link
Copy Markdown
Member

Problem

passive_order_rejected is the code that tells a client "the venue refused to rest this order for a post-only reason" — which is the client's cue that the price level may be re-placed. classifyPassiveOrderError was an allowlist for two post-only faults (would-cross, post-only-unsupported) with a catch-all that assigned passive_order_rejected to everything else that failed during a passive submission.

ccxt raises InsufficientFunds from the submission call itself, so a plain balance shortfall was reported as a post-only rejection. The client then re-placed the rung, which failed identically, on repeat. In production this ran at 800–1500 failed venue submissions per hour across two markets for as long as the account was short of quote currency — no incorrect exposure (it fails closed), but wasted venue order-rate budget and heavy log/archive noise.

Change

Balance and credential faults on the passive create path now report their own stable error codes instead of a passive_* code:

  • ccxt.InsufficientFundsInsufficientFunds: (FAILED_PRECONDITION)
  • ccxt.AuthenticationErrorAuthenticationError: (UNAUTHENTICATED); PermissionDenied subclasses it, so one check covers both

Both are already established codes in the shared error vocabulary, and the existing convention is that the stable code leads the message — which matters, because clients recover the error kind by reading the leading token. Routing these through the generic Order Creation failed: … fallback would not work: that message leads with Order, which classifies as unknown, and returns INTERNAL for what is a precondition failure.

Detection is by typed ccxt class, not message text. The text heuristics remain only for would-cross and post-only-unsupported, where some venues report without a distinct class. The new checks precede those, which is safe: the class hierarchies are disjoint (verified against the ccxt prototype chain).

The classification decision stays in one place — the handler emits whatever code the classifier returns.

Not changed

The submission state gate (not_attempted/in_flight/placed), the would-cross and unsupported heuristics, telemetry/archive emission, and the generic fallback are all untouched. No retry, backoff, or balance pre-check is added: the broker reports what the venue said, and the client decides what to do about it.

Verification

bun test test/passive-order.test.ts        13 pass, 0 fail
bun test test/grpc-status.test.ts test/order-error-detail.test.ts    5 pass, 0 fail
bun test                                   480 pass, 0 fail
bunx tsc --noEmit                          clean
bunx biome check src test                  no errors

New coverage: a balance fault yields an InsufficientFunds:-leading message with no passive_ prefix; authentication and permission faults yield AuthenticationError:; existing would-cross / unsupported / generic-rejection classifications and the pre-submission and post-submission submission-state behavior are unchanged.

Release ordering

The consuming client needs a companion change to treat a venue balance fault as terminal rather than retryable. Without it, the client's stock retry path makes a bounded burst of doomed submissions instead of an unbounded loop — an improvement, but the two should ship together for the intended single-attempt behavior.

Companion client-side change: usherlabs/fiet-maker#965

Summary by CodeRabbit

  • Bug Fixes
    • Improved passive order error reporting for authentication failures and insufficient-funds errors.
    • Preserved stable error types instead of labeling these issues as generic passive-order rejections.
    • Corrected gRPC status mappings for authentication and insufficient-funds failures.
    • Maintained existing handling for post-only, unsupported, and other rejected-order errors.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Passive-order error handling now preserves authentication and insufficient-funds broker errors, assigns stable error codes, maps those codes to gRPC statuses, and adds test coverage for the updated behavior.

Changes

Passive-order error propagation

Layer / File(s) Summary
Stable error classification and gRPC mapping
src/helpers/passive-order.ts, src/helpers/grpc/status.ts, src/handlers/execute-action/orders.ts
Authentication and insufficient-funds errors receive stable codes, which are preserved by order rejection handling and mapped to specific gRPC statuses.
Error propagation tests
test/grpc-status.test.ts, test/passive-order.test.ts
Tests verify the new status mappings and ensure broker error codes are not rewritten as passive-order rejection codes; one fixture assignment is reformatted.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • usherlabs/cex-broker#86: Both changes extend passive-order error classification and downstream gRPC status mapping.

Suggested reviewers: csmithington

Poem

I’m a rabbit with errors kept neat,
Stable codes hop on swift little feet.
Auth gets its status, funds know their place,
No passive disguise in the rejection race.
Tests nibble the edges—แล้ว, all is bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: preserving venue error codes during passive order submissions.
✨ 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 fix/passive-error-taxonomy

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@src/handlers/execute-action/orders.ts`:
- Around line 212-214: Update the stable message composition in the
passive-order error handler around classifyPassiveOrderError and
rejectWithGrpcError to use a sanitized error detail without its class-name
prefix. Keep stableErrorCode as the sole error class identifier while preserving
the remaining sanitized detail.
🪄 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: 694b3fbe-5301-4352-9984-b54dbe3bfd31

📥 Commits

Reviewing files that changed from the base of the PR and between 2698f48 and eb07c5a.

📒 Files selected for processing (5)
  • src/handlers/execute-action/orders.ts
  • src/helpers/grpc/status.ts
  • src/helpers/passive-order.ts
  • test/grpc-status.test.ts
  • test/passive-order.test.ts
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-04-14T06:47:01.283Z
Learnt from: csmithington
Repo: usherlabs/cex-broker PR: 38
File: src/client.dev.ts:0-0
Timestamp: 2026-04-14T06:47:01.283Z
Learning: In this codebase, gRPC action constants such as `FetchTicker`, `FetchFees`, and `FetchAccountId` should be sourced from `src/helpers/constants.ts` and imported from there (e.g., used by both `src/client.dev.ts` and `src/server.ts`). Do not import or reference generated proto TypeScript artifacts (for example `./proto/cex_broker/Action`), since those generated files are git-ignored and won’t be available/committed consistently.

Applied to files:

  • src/handlers/execute-action/orders.ts
  • src/helpers/grpc/status.ts
  • src/helpers/passive-order.ts
🔇 Additional comments (4)
src/helpers/passive-order.ts (1)

13-16: LGTM!

Also applies to: 48-61

src/helpers/grpc/status.ts (1)

6-11: LGTM!

test/grpc-status.test.ts (1)

12-17: LGTM!

test/passive-order.test.ts (1)

206-242: LGTM!

Also applies to: 259-261

Comment on lines +212 to +214
const stableErrorCode = classifyPassiveOrderError(error);
return rejectWithGrpcError(ctx, error, {
message: `${passiveErrorCode}: ${sanitizeErrorDetail(error)}`,
message: `${stableErrorCode}: ${sanitizeErrorDetail(error)}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid duplicating the CCXT class in the stable message.

sanitizeErrorDetail(error) already includes errorClassName(error), so this produces messages such as InsufficientFunds: InsufficientFunds: ... and AuthenticationError: AuthenticationError: .... Use a sanitized detail that omits the class-name prefix when composing the stable code message.

🤖 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 `@src/handlers/execute-action/orders.ts` around lines 212 - 214, Update the
stable message composition in the passive-order error handler around
classifyPassiveOrderError and rejectWithGrpcError to use a sanitized error
detail without its class-name prefix. Keep stableErrorCode as the sole error
class identifier while preserving the remaining sanitized detail.

@outerlook
outerlook merged commit 31e9136 into develop Jul 30, 2026
5 checks passed
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.

1 participant