Skip to content

Commit 5cfba8b

Browse files
gaoyu06claude
andcommitted
fix(claude): treat a failed --resume result as resume_failed (fixes yolo startup)
Reproduced by running the CLI directly: yolo respawn with --resume of a session the gateway didn't persist makes claude emit a result frame {subtype:error_during_execution, errors:["No conversation found …"]} and exit — which onResult surfaced as a scary "error_during_execution" card, reading as a "session startup failure". Now onResult inspects the result's errors/result text: a "No conversation found" failure emits resume_failed (so the store restarts fresh, preserving the desktop transcript) instead of an error card. Adds ResultFrame.errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent da40ae5 commit 5cfba8b

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

src/lib/backends/claude-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ export interface ResultFrame {
138138
usage?: ApiUsage;
139139
modelUsage?: Record<string, { contextWindow?: number; inputTokens?: number; outputTokens?: number }>;
140140
permission_denials?: unknown[];
141+
/** Turn-level error strings (e.g. a failed --resume: "No conversation found …"). */
142+
errors?: string[];
141143
}
142144

143145
// --- control protocol ----------------------------------------------------------

src/lib/backends/claude.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,26 @@ describe('claude adapter: interrupt / modes / results', () => {
871871
expect(String(auth[0].message)).toContain('/login');
872872
});
873873

874+
it('a failed --resume result becomes resume_failed, not a scary error card', () => {
875+
const { lines } = makeIo();
876+
const adapter = createClaudeAdapter();
877+
boot(adapter, lines);
878+
// yolo respawn with --resume of an unpersisted session: the CLI emits an
879+
// error_during_execution result carrying "No conversation found …" then exits.
880+
const ev = adapter.translate({
881+
type: 'result',
882+
subtype: 'error_during_execution',
883+
is_error: true,
884+
errors: ['No conversation found with session ID: 00000000-1111-2222-3333-444444444444'],
885+
num_turns: 0,
886+
session_id: SID
887+
});
888+
expect(ev).toEqual([
889+
{ type: 'resume_failed' },
890+
{ type: 'status', message: 'ready' }
891+
]);
892+
});
893+
874894
it('shutdown is accepted silently; unsupported ops are refused', () => {
875895
const { lines } = makeIo();
876896
const adapter = createClaudeAdapter();

src/lib/backends/claude.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,16 @@ export function createClaudeAdapter(): EngineAdapter {
801801
events.push({ type: 'context_usage', tokens: lastContext, cost: frame.total_cost_usd });
802802
}
803803
if ((frame.is_error || frame.subtype !== 'success') && !wasInterrupting) {
804-
events.push(errorEvent(str(frame.result) || frame.subtype || 'error'));
804+
const detail =
805+
str(frame.result) ||
806+
(Array.isArray(frame.errors) ? frame.errors.map(str).filter(Boolean).join('; ') : '') ||
807+
frame.subtype ||
808+
'error';
809+
// A failed --resume surfaces here (error_during_execution + "No conversation
810+
// found …") right before the child exits. Treat it as a resume failure so
811+
// the store restarts fresh instead of showing a scary error card + looping.
812+
if (/No conversation found with session ID/i.test(detail)) events.push({ type: 'resume_failed' });
813+
else events.push(errorEvent(detail));
805814
}
806815
events.push({ type: 'status', message: 'ready' });
807816
return events;

0 commit comments

Comments
 (0)