From 67f9d631bd73920601e10dcfc889fd24cb5eca67 Mon Sep 17 00:00:00 2001 From: Tafari Higgs Date: Thu, 5 Feb 2026 13:32:38 -0500 Subject: [PATCH] Fix hooks to fail gracefully instead of blocking user prompts Three issues fixed: 1. session-init handler: Worker 500 errors caused `throw`, which hook-command.ts caught and exited with code 2 (blocking error). This blocked the user's prompt entirely with: "Hook error: Error: Session initialization failed: 500" Fix: Log the failure and return SUCCESS so the prompt proceeds. 2. user-message handler: Referenced nonexistent constant HOOK_EXIT_CODES.USER_MESSAGE_ONLY (undefined). Also used console.error() for informational output, which Claude Code may flag as an error. Fix: Use process.stderr.write() and return HOOK_EXIT_CODES.SUCCESS explicitly. 3. Both handlers threw on HTTP errors from the worker, causing exit code 2 (blocking). Memory plugin failures should never prevent users from using Claude Code. All worker HTTP failures now log and return gracefully. Co-Authored-By: Claude Opus 4.6 --- src/cli/handlers/session-init.ts | 7 +++++-- src/cli/handlers/user-message.ts | 21 ++++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/cli/handlers/session-init.ts b/src/cli/handlers/session-init.ts index 2e9679bd..095f9497 100644 --- a/src/cli/handlers/session-init.ts +++ b/src/cli/handlers/session-init.ts @@ -43,7 +43,9 @@ export const sessionInitHandler: EventHandler = { }); if (!initResponse.ok) { - throw new Error(`Session initialization failed: ${initResponse.status}`); + // Log but don't throw - a worker 500 should not block the user's prompt + logger.failure('HOOK', `Session initialization failed: ${initResponse.status}`, { contentSessionId: sessionId, project }); + return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS }; } const initResult = await initResponse.json() as { @@ -86,7 +88,8 @@ export const sessionInitHandler: EventHandler = { }); if (!response.ok) { - throw new Error(`SDK agent start failed: ${response.status}`); + // Log but don't throw - SDK agent failure should not block the user's prompt + logger.failure('HOOK', `SDK agent start failed: ${response.status}`, { sessionDbId, promptNumber }); } } else if (input.platform === 'cursor') { logger.debug('HOOK', 'session-init: Skipping SDK agent init for Cursor platform', { sessionDbId, promptNumber }); diff --git a/src/cli/handlers/user-message.ts b/src/cli/handlers/user-message.ts index 061e3dfb..248efb0f 100644 --- a/src/cli/handlers/user-message.ts +++ b/src/cli/handlers/user-message.ts @@ -1,8 +1,8 @@ /** * User Message Handler - SessionStart (parallel) * - * Extracted from user-message-hook.ts - displays context info to user via stderr. - * Uses exit code 3 to show user message without injecting into Claude's context. + * Displays context info to user via stderr. + * Uses exit code 0 (SUCCESS) - stderr is not shown to Claude with exit 0. */ import { basename } from 'path'; @@ -26,21 +26,24 @@ export const userMessageHandler: EventHandler = { ); if (!response.ok) { - throw new Error(`Failed to fetch context: ${response.status}`); + // Don't throw - context fetch failure should not block the user's prompt + return { exitCode: HOOK_EXIT_CODES.SUCCESS }; } const output = await response.text(); - // Write to stderr for user visibility (Claude Code UI shows stderr) - console.error( - "\n\n" + String.fromCodePoint(0x1F4DD) + " Claude-Mem Context Loaded\n" + - " " + String.fromCodePoint(0x2139, 0xFE0F) + " Note: This appears as stderr but is informational only\n\n" + + // Write to stderr for user visibility + // Note: Using process.stderr.write instead of console.error to avoid + // Claude Code treating this as a hook error. The actual hook output + // goes to stdout via hook-command.ts JSON serialization. + process.stderr.write( + "\n\n" + String.fromCodePoint(0x1F4DD) + " Claude-Mem Context Loaded\n\n" + output + - "\n\n" + String.fromCodePoint(0x1F4A1) + " New! Wrap all or part of any message with ... to prevent storing sensitive information in your observation history.\n" + + "\n\n" + String.fromCodePoint(0x1F4A1) + " Wrap any message with ... to prevent storing sensitive information.\n" + "\n" + String.fromCodePoint(0x1F4AC) + " Community https://discord.gg/J4wttp9vDu" + `\n` + String.fromCodePoint(0x1F4FA) + ` Watch live in browser http://localhost:${port}/\n` ); - return { exitCode: HOOK_EXIT_CODES.USER_MESSAGE_ONLY }; + return { exitCode: HOOK_EXIT_CODES.SUCCESS }; } };