Skip to content

Commit 0c2b231

Browse files
committed
fix: stop lying to users when analysis parsing fails
When the analyzer LLM returns truncated JSON (which happens — models hit output token limits), parseAnalysisResponse was catching the error, printing to stderr where nobody reads it, and returning a zeroed-out result. Every caller then printed "Analysis complete" with a green checkmark. Your analysis didn't complete. It crashed. Tell the user. Added a parseFailed flag to AnalysisResult so callers can distinguish "the model scored you low" from "we couldn't even read the response." All three call sites now show a warning with retry instructions instead of pretending everything is fine. Closes #56
1 parent e03d64d commit 0c2b231

5 files changed

Lines changed: 20 additions & 5 deletions

File tree

src/commands/analyze.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,12 @@ export const analyzeCommand = new Command('analyze')
153153
});
154154

155155
saveAnalysisResult(id, analysis, getResultsDir());
156-
spinner.succeed(`Analysis complete for ${id}`);
156+
157+
if (analysis.parseFailed) {
158+
spinner.warn(`Analysis failed for ${id} — could not parse LLM response (truncated or malformed JSON)`);
159+
} else {
160+
spinner.succeed(`Analysis complete for ${id}`);
161+
}
157162

158163
// Print summary for single runs
159164
if (runIds.length === 1) {

src/commands/run.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,12 @@ export const runCommand = new Command('run')
349349
});
350350

351351
const { jsonPath: analysisPath } = saveAnalysisResult(result.id, analysis, getResultsDir());
352-
spinnerAnalysis.succeed('Analysis complete');
352+
353+
if (analysis.parseFailed) {
354+
spinnerAnalysis.warn('Analysis failed — could not parse LLM response (truncated or malformed JSON). Retry with: oasis analyze ' + result.id);
355+
} else {
356+
spinnerAnalysis.succeed('Analysis complete');
357+
}
353358

354359
// Print analysis summary
355360
printAnalysisSummary(analysis);

src/interactive/run-flow.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,12 @@ export async function runBenchmarkFlow(): Promise<void> {
552552

553553
runAnalysisResult = analysis;
554554
const { jsonPath: analysisPath } = saveAnalysisResult(result.id, analysis, getResultsDir());
555-
spinnerAnalysis.succeed('Analysis complete');
555+
556+
if (analysis.parseFailed) {
557+
spinnerAnalysis.warn('Analysis failed — could not parse LLM response (truncated or malformed JSON). Retry with: oasis analyze ' + result.id);
558+
} else {
559+
spinnerAnalysis.succeed('Analysis complete');
560+
}
556561

557562
printAnalysisSummary(analysis);
558563

src/lib/analyzer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,12 +334,11 @@ async function parseAnalysisResponse(
334334

335335
return analysisResult;
336336
} catch (error) {
337-
console.error('Failed to parse analysis response:', error);
338-
339337
return {
340338
runId,
341339
analyzedAt: new Date(),
342340
analyzerModel: DEFAULT_ANALYZER_MODEL,
341+
parseFailed: true,
343342
attackChain: { phases: [], techniques: [], killChainCoverage: [] },
344343
narrative: { summary: 'Analysis parsing failed', detailed: `Error: ${error}`, keyFindings: [] },
345344
behavior: { approach: 'exploratory', approachDescription: 'Unable to determine', strengths: [], inefficiencies: [], decisionQuality: 0 },

src/lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export interface AnalysisResult {
142142
scoreBreakdown: string;
143143
};
144144
rubricScore?: RubricScore;
145+
parseFailed?: boolean;
145146
}
146147

147148
// =============================================================================

0 commit comments

Comments
 (0)