Skip to content

fix(ENG-12540): prevent unhandled rejection cascade on ONNX load failure#37

Merged
hiskudin merged 3 commits intomainfrom
fix/unhandled-rejection-cascade
Apr 1, 2026
Merged

fix(ENG-12540): prevent unhandled rejection cascade on ONNX load failure#37
hiskudin merged 3 commits intomainfrom
fix/unhandled-rejection-cascade

Conversation

@hiskudin
Copy link
Copy Markdown
Collaborator

@hiskudin hiskudin commented Apr 1, 2026

Context

When the ONNX model fails to load (e.g. on Alpine where onnxruntime-node native binary can't load), void inFlight.finally(...) in _loadModel() creates an unhandled promise rejection. The NestJS global handler wraps and re-emits it, creating a recursive cascade ~130 levels deep on every request that triggers defender.

The growing error strings ("Unhandled rejection: [object Promise] reason: Error: Unhandled rejection: ..." × 130) consume memory continuously under production load, contributing to pod restarts.

Fix

Replace void inFlight.finally(...) with inFlight.finally(...).catch(() => {}).

The .catch(() => {}) silences the duplicate rejection from the .finally() chain. The actual error still propagates correctly through await inFlightloadModel() catch → caller's try/catch.

One-line diff

- void inFlight.finally(() => _loadingPromises.delete(this.modelPath));
+ inFlight.finally(() => _loadingPromises.delete(this.modelPath)).catch(() => {});

🤖 Generated with Claude Code


Summary by cubic

Stop the unhandled rejection cascade when ONNX model loading fails, eliminating error storms and memory growth under load. Addresses ENG-12540 by catching the .finally() rejection while keeping the original error flow intact.

  • Bug Fixes
    • Add .catch(() => {}) to the inFlight.finally(...) chain; the original error still propagates via await inFlight.
    • Add a test that asserts no unhandledRejection on model load failure and uses try/finally to reliably clean up the listener; document the catch inline.

Written for commit f11b785. Summary will update on new commits.

`void inFlight.finally(...)` discarded the promise returned by
`.finally()`. When `inFlight` rejects (e.g. onnxruntime-node native
binary fails to load), the `.finally()` promise also rejects with no
handler — causing an unhandled rejection. The NestJS global handler
wraps and re-emits it, creating a recursive cascade (~130 deep) on
every request that triggers defender. The growing error strings
consume memory, contributing to pod restarts.

The actual error still propagates correctly through `await inFlight`
→ `loadModel()` catch → caller's try/catch. The `.catch(() => {})`
only silences the duplicate rejection from the `.finally()` chain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hiskudin hiskudin requested a review from a team as a code owner April 1, 2026 09:23
Copilot AI review requested due to automatic review settings April 1, 2026 09:23
cubic-dev-ai[bot]
cubic-dev-ai Bot previously approved these changes Apr 1, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

Auto-approved: Isolated fix to prevent unhandled promise rejections and associated memory growth during ONNX load failures.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR prevents an unhandled promise rejection cascade when the ONNX model load fails (e.g., native onnxruntime-node binary can’t load), by ensuring the promise created by the .finally() cleanup path is explicitly handled.

Changes:

  • Replace void inFlight.finally(...) with inFlight.finally(...).catch(() => {}) to prevent an unhandled rejection from the .finally() chain on load failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/classifiers/onnx-classifier.ts
Comment thread src/classifiers/onnx-classifier.ts
Comment thread src/classifiers/onnx-classifier.ts
@hiskudin hiskudin changed the title fix: prevent unhandled rejection cascade on ONNX load failure fix(ENG-12540): prevent unhandled rejection cascade on ONNX load failure Apr 1, 2026
- Add comment explaining why .catch(() => {}) is needed
- Add test that verifies no unhandledRejection is emitted when the
  ONNX model fails to load

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="specs/onnx-classifier.spec.ts">

<violation number="1" location="specs/onnx-classifier.spec.ts:87">
P3: Ensure the global unhandledRejection listener is always removed even when the test fails. Wrap the test body in a try/finally so the listener is cleaned up on all exits.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread specs/onnx-classifier.spec.ts Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Auto-approved: Fixes a critical production issue involving recursive error cascades and memory growth. The change is isolated, follows standard promise handling practices, and includes a regression test.

@hiskudin hiskudin merged commit 52c99e3 into main Apr 1, 2026
4 checks passed
@hiskudin hiskudin deleted the fix/unhandled-rejection-cascade branch April 1, 2026 11:20
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.

3 participants