Skip to content

Commit df72682

Browse files
fix(security-agent): add SANDBOX_FAILED classification and reduce Sentry noise (#934)
2 parents d688b17 + 0c76d82 commit df72682

3 files changed

Lines changed: 63 additions & 11 deletions

File tree

src/lib/security-agent/core/error-classification.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@ describe('classifyAnalysisError', () => {
7575
});
7676
});
7777

78+
describe('SANDBOX_FAILED', () => {
79+
it.each([
80+
'ConfigInvalidError: bad file reference: "{file:.opencode/prompts/agents/planner.txt}"',
81+
'Failed to create workspace directory /tmp/sandbox-123',
82+
'Failed to create kilo CLI session for repo foo/bar',
83+
'prepareSession call failed with HTTP 500',
84+
'FileSystemError: mkdir operation failed with exit code NaN',
85+
])('classifies %j as SANDBOX_FAILED', message => {
86+
expect(classifyAnalysisError(new Error(message)).code).toBe('SANDBOX_FAILED');
87+
});
88+
89+
it('does not match generic filesystem or config errors without sandbox context', () => {
90+
expect(classifyAnalysisError(new Error('Invalid config option')).code).not.toBe(
91+
'SANDBOX_FAILED'
92+
);
93+
expect(classifyAnalysisError(new Error('ENOENT: no such file or directory')).code).not.toBe(
94+
'SANDBOX_FAILED'
95+
);
96+
});
97+
});
98+
7899
describe('UNKNOWN', () => {
79100
it('returns UNKNOWN for unrecognized errors', () => {
80101
const result = classifyAnalysisError(new Error('something unexpected'));

src/lib/security-agent/core/error-classification.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ const NOT_FOUND_PATTERNS = [
3131
/\bno such\b.*\brepository\b/i,
3232
];
3333

34+
const SANDBOX_PATTERNS = [
35+
/\bConfigInvalidError\b/i,
36+
/\bFailed to create.*workspace\b/i,
37+
/\bFailed to create kilo CLI session\b/i,
38+
/\bprepareSession\b.*\bfailed\b/i,
39+
/\bFileSystemError\b/i,
40+
];
41+
3442
/**
3543
* Classify a raw error into a structured code and user-friendly message.
3644
*
@@ -63,6 +71,13 @@ export function classifyAnalysisError(error: unknown): ClassifiedError {
6371
};
6472
}
6573

74+
if (SANDBOX_PATTERNS.some(p => p.test(raw))) {
75+
return {
76+
code: 'SANDBOX_FAILED',
77+
userMessage: 'Sandbox analysis failed to start. Please try again later.',
78+
};
79+
}
80+
6681
return {
6782
code: 'UNKNOWN',
6883
userMessage: 'An unexpected error occurred. Please try again.',

src/lib/security-agent/services/triage-service.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,25 +285,41 @@ export async function triageSecurityFinding(options: {
285285
correlationId,
286286
findingId: finding.id,
287287
status: result.status,
288+
model,
288289
});
289-
captureException(new Error(`Triage API error: ${result.status}`), {
290-
tags: { operation: 'triageSecurityFinding' },
291-
extra: {
292-
findingId: finding.id,
293-
status: result.status,
294-
error: result.error,
295-
correlationId,
296-
},
297-
});
290+
291+
// Provider-side errors we expect when users pick models that are
292+
// delisted (404), rate-limited (408/429), or when the provider is
293+
// down (5xx). These are not actionable on our side.
294+
// All other statuses (e.g. 400/401/403) may indicate bugs in our
295+
// request or auth path and should still reach Sentry.
296+
const isProviderError =
297+
result.status === 404 ||
298+
result.status === 408 ||
299+
result.status === 429 ||
300+
result.status >= 500;
301+
302+
if (!isProviderError) {
303+
captureException(new Error(`Triage API error: ${result.status}`), {
304+
tags: { operation: 'triageSecurityFinding' },
305+
extra: {
306+
findingId: finding.id,
307+
status: result.status,
308+
error: result.error,
309+
model,
310+
correlationId,
311+
},
312+
});
313+
}
298314

299315
span.setAttribute('security_agent.status', 'error');
300316
span.setAttribute('security_agent.is_fallback', true);
301317

302318
addBreadcrumb({
303319
category: 'security-agent.triage',
304-
message: 'Triage fallback used',
320+
message: `Triage fallback used (model=${model}, status=${result.status})`,
305321
level: 'warning',
306-
data: { correlationId, findingId: finding.id, isFallback: true },
322+
data: { correlationId, findingId: finding.id, model, isFallback: true },
307323
});
308324

309325
return createFallbackTriage(`API error: ${result.status}`);

0 commit comments

Comments
 (0)