fix(impact): return structured error + partial results instead of crashing (#321)#345
Conversation
…shing (#321) - Wrap impact() in try-catch to return structured error JSON instead of process crash (SIGSEGV/exit 139) - Extract core logic to _impactImpl() for clean error boundary - Break out of depth traversal loop on query failure, return partial results collected so far (previously silently swallowed errors) - Add 'partial' flag to response when traversal was interrupted - Add try-catch in CLI impactCommand with structured error output - Improve formatImpactResult to show suggestion text and partial warning - Add 3 new unit tests for error/suggestion/partial scenarios Fixes #321
|
@ShunsukeHayashi is attempting to deploy a commit to the NexusCore Team on Vercel. A member of the Team first needs to authorize it. |
|
What's the most common error you tend to see from exceptions? |
|
From running impact across 25 repos in production, the most common exceptions:
The |
|
@claude ultra-think while you are reviewing this pr and you are tasked to find bugs, code quality issues, architectural fit and performance issues |
|
Claude finished @magyargergo's task in 2m 43s —— View job PR Review: fix(impact) — structured error + partial resultsTasks
SummaryThe intent is sound — turning crashes into structured errors is correct. The Bugs1. Inconsistent
// ERROR response — target is a string
{ error: "...", target: "MySymbol", ... }
// SUCCESS response — target is an object
{ target: { id: "...", name: "MySymbol", type: "Function", filePath: "..." }, ... }The formatter handles it safely because it returns early on 2.
More concretely: this // outer catch — this partial flag is dead in the formatter
return {
error: err?.message || 'Impact analysis failed',
partial: true, // <-- never displayed; misleading for zero-result failures
...
};3.
const backend = await getBackend(); // <-- uncaught
try {
const result = await backend.callTool('impact', { ... });4.
Code Quality5. Duplicate parameter interface between
// Both functions currently repeat this verbatim:
params: {
target: string;
direction: 'upstream' | 'downstream';
maxDepth?: number;
relationTypes?: string[];
includeTests?: boolean;
minConfidence?: number;
}6. Error response schema inconsistency between the two catch layers The outer Architecture7. Two error boundaries doing overlapping work Since
Currently neither layer has a comment explaining the intended separation. Tests8. Weakened existing test without justification
// Before (strict):
expect(formatImpactResult({ error: 'bad request' })).toBe('Error: bad request');
// After (relaxed):
expect(formatImpactResult({ error: 'bad request' })).toContain('Error: bad request');
9. No test for the case where The partial warning test ( Minor
VerdictThe core fix — |
|
Could you please look at this report? 🙏 |
Per @claude's review (requested by @magyargergo): - [BUG 1] Consistent target field shape: error responses now return {name: string} instead of raw string, matching success response schema - [BUG 2] Remove misleading partial:true from total-failure responses (partial is only meaningful when some depth levels succeeded) - [BUG 3] Move getBackend() inside try-catch in impactCommand so backend init failures return structured JSON instead of crashing - [BUG 4] Safe error message extraction: use instanceof Error check to handle thrown strings correctly (err?.message is undefined for non-Error thrown values) - [MINOR] Add radix argument to parseInt (10)
|
Addressed all 4 bugs from @claude's review: Fixed:
Acknowledged (scope):
|
|
@claude recommend integration test for this PR |
|
Claude finished @magyargergo's task in 2m 14s —— View job Tasks
Integration Test RecommendationsThe existing unit tests in 1.
|
| # | Test | Why critical |
|---|---|---|
| 1 | Nonexistent symbol → structured error | Covers the "symbol not found" (~40%) failure mode, the most common production error |
| 4 | CLI exit code ≠ 139 | Direct regression guard for the SIGSEGV bug that motivated the whole PR |
| 3 | Partial results on traversal failure | The traversalComplete + break logic has no integration coverage at all |
| 2 | No partial on total failure |
Guards the misleading-flag fix from the review |
| 6 | target shape consistency |
Guards the schema fix from review bug #1 |
| 5 | CLI happy path shape | Baseline e2e confidence in the structured output format |
Tests 1, 2, and 6 slot cleanly into the existing withTestLbugDB block in local-backend-calltool.test.ts with no new dependencies. Test 3 requires vi.spyOn on executeQuery. Tests 4 and 5 extend the existing cli-e2e.test.ts and depend on analyze having run — run them in the same describe block after the analyze test.
CI Report✅ All checks passed Pipeline Status
Test Results
✅ All 2711 tests passed 20 test(s) skipped — expand for detailsIntegration:
Unit:
Code CoverageCombined (Unit + Integration)
Coverage breakdown by test suiteUnit Tests
Integration Tests
📋 View full run · Generated by CI |
|
Can you please also resolve the merge conflicts? 🙏 |
Resolved merge conflicts in:
- src/cli/tool.ts: keep getBackend() inside try-catch, instanceof Error check, radix 10
- src/mcp/local/local-backend.ts: keep {name: string} target shape, remove partial:true on total failures
Other upstream changes pulled in: #330 (permissions prepack), #324 (fd1 stdout)
Per @claude's recommendation (requested by @magyargergo): - impact: structured error for unknown symbol (no crash) - impact: error response has consistent {name: string} target shape - impact: partial:true only set when some results were collected Tests use existing withTestLbugDB + seeded graph fixture.
|
Done — resolved merge conflicts and added integration tests per @claude's recommendation. Merge conflicts resolved:
Integration tests added (
Uses the existing |
…shing (abhigyanpatwari#321) (abhigyanpatwari#345) * fix(impact): return structured error + partial results instead of crashing (abhigyanpatwari#321) - Wrap impact() in try-catch to return structured error JSON instead of process crash (SIGSEGV/exit 139) - Extract core logic to _impactImpl() for clean error boundary - Break out of depth traversal loop on query failure, return partial results collected so far (previously silently swallowed errors) - Add 'partial' flag to response when traversal was interrupted - Add try-catch in CLI impactCommand with structured error output - Improve formatImpactResult to show suggestion text and partial warning - Add 3 new unit tests for error/suggestion/partial scenarios Fixes abhigyanpatwari#321 * fix: address review feedback — 4 bugs from @claude review Per @claude's review (requested by @magyargergo): - [BUG 1] Consistent target field shape: error responses now return {name: string} instead of raw string, matching success response schema - [BUG 2] Remove misleading partial:true from total-failure responses (partial is only meaningful when some depth levels succeeded) - [BUG 3] Move getBackend() inside try-catch in impactCommand so backend init failures return structured JSON instead of crashing - [BUG 4] Safe error message extraction: use instanceof Error check to handle thrown strings correctly (err?.message is undefined for non-Error thrown values) - [MINOR] Add radix argument to parseInt (10) * test: add integration tests for impact error handling (abhigyanpatwari#321) Per @claude's recommendation (requested by @magyargergo): - impact: structured error for unknown symbol (no crash) - impact: error response has consistent {name: string} target shape - impact: partial:true only set when some results were collected Tests use existing withTestLbugDB + seeded graph fixture.
Problem
The
impactcommand crashes with SIGSEGV (exit 139) on certain repositories (#321). When the underlying graph query fails (e.g., due to corrupted index, large traversal, or native binding issues), the entire Node.js process terminates instead of returning a useful error.This makes
impactunreliable in production workflows — both for CLI users and for MCP-connected agents that depend ongitnexus_impact()returning structured results.Root Cause
impactCommand(CLI) — No try-catch aroundbackend.callTool('impact', ...). Any thrown error kills the process.impact()(LocalBackend) — The depth traversal catch block (catch (e) { logQueryError(...) }) silently swallows errors and continues, which means:Changes
1. Structured error boundary in
impact()→_impactImpl(){ "error": "Impact analysis failed", "target": "symbolName", "direction": "upstream", "impactedCount": 0, "risk": "UNKNOWN", "partial": true, "suggestion": "Try gitnexus context <symbol> as a fallback" }2. Partial results on depth traversal failure
partial: trueflag is added to the response so callers know the analysis was interrupted.3. CLI error handling
impactCommandnow catches errors and outputs structured JSON instead of crashing.4. Formatter improvements
formatImpactResultnow displays:⚠️ Partial resultswarning when traversal was interruptedFiles changed
src/mcp/local/local-backend.ts_impactImpl(), add error boundary,partialflag, break on traversal errorsrc/cli/tool.tsimpactCommandwith structured error outputsrc/cli/eval-server.tsformatImpactResultfor suggestion + partial warningtest/unit/eval-formatters.test.tsTesting
formatImpactResulttests updated for compatibilityContext
We've been running GitNexus across 25+ repositories in production and built gitnexus-stable-ops as an operational toolkit. The
gitnexus-safe-impact.shscript in that toolkit was our workaround for this issue — this PR brings the fix into the core.Related: #344