thedotmack/claude-mem · info
Hook input unavailable, skipping hook
Error message
Hook input unavailable, skipping hook: ${error instanceof Error ? error.message : error} What it means
After AdapterRejectedInput, hookCommand also checks isNonBlockingHookInputError: errors meaning hook input was simply unavailable (no/empty stdin, unreadable stdin stream) rather than invalid. These are logged with this message, a no-op result is emitted, and the hook exits with SUCCESS — the hook never blocks the host session on missing input.
Solutions
- When testing, always pipe a sample payload: cat sample.json | claude-mem hook ...
- Verify the hook entry in settings.json is registered on an event that actually provides input JSON
- Check that no shell wrapper (script/sudo) detaches stdin before the hook runs
- Confirm Claude Code version compatibility so it actually writes the payload to the hook's stdin
Example fix
// before
claude-mem hook --event Stop # no stdin -> this warning
// after
echo '{"session_id":"abc","transcript_path":"/tmp/t.jsonl"}' | claude-mem hook --event Stop Defensive patterns
Strategy: fallback
Validate before calling
let raw = '';
try { raw = readFileSync(0, 'utf8'); } catch { raw = ''; }
if (!raw.trim()) {
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
process.exit(HOOK_EXIT_CODES.SUCCESS);
} Type guard
function hasStdinPayload(): boolean { try { return readFileSync(0, 'utf8').trim().length > 0; } catch { return false; } } Try / catch
try { /* pipeline */ } catch (error) {
if (isNonBlockingHookInputError(error)) {
logger.warn('HOOK', `Hook input unavailable, skipping hook: ${error instanceof Error ? error.message : error}`);
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
} Prevention
- Never invoke hook commands without piped stdin in scripts/tests
- Check hook registration so the platform always supplies a payload
- Avoid wrappers (sudo, script) that detach stdin
- Emit a no-op result and exit 0 so the host session is never blocked
When it happens
Trigger: The hook process receives no stdin payload: invoked outside a hook context, stdin closed early by the host, TTY-attached manual run with no piped input, or the platform writes nothing because the event carried no data.
Common situations: Running `claude-mem hook` directly in a terminal during debugging; hook registered on an event that sends no body; shell redirection mistakes in settings.json; platform bug closing stdin prematurely.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Adapter rejected input
- Could not persist SessionEnd request for worker recovery
- Stop hook: failed to extract last assistant message for…
- Worker unavailable, skipping hook
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/a8615455fa1ad224.
Report an issue: GitHub.
Appendix: source
Thrown at src/cli/hook-command.ts:141
// To bypass the buffer for a specific write, use emitDiagnostic /
// emitBlockingError from src/shared/hook-io.ts. Direct process.stderr.write
// calls are buffered.
const stderrBuffer = installHookStderrBuffer();
const adapter = getPlatformAdapter(platform);
const handler = getEventHandler(event);
try {
return await executeHookPipeline(adapter, handler, platform, options);
} catch (error) {
if (error instanceof AdapterRejectedInput) {
logger.warn('HOOK', `Adapter rejected input (${error.reason}), skipping hook`);
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
if (isNonBlockingHookInputError(error)) {
logger.warn('HOOK', `Hook input unavailable, skipping hook: ${error instanceof Error ? error.message : error}`);
emitModelContext(adapter, buildNoOpResult(event));
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
if (isWorkerUnavailableError(error)) {
logger.warn('HOOK', `Worker unavailable, skipping hook: ${error instanceof Error ? error.message : error}`);
// EXIT_SIGNAL per CLAUDE.md: transient worker errors exit 0 to avoid
// Windows Terminal tab accumulation. The fail-loud counter (worker-utils
// recordWorkerUnreachable) handles the surface-after-N-failures path and
// emits the threshold-gated hook_failed telemetry internally. Awaited:
// when the count JUST reaches the threshold it sends the event and then
// exits 2; exitGraceful below would kill a pending POST mid-flight.
await recordWorkerUnreachable();
exitGraceful(options);
return HOOK_EXIT_CODES.SUCCESS;
}
logger.error('HOOK', `Hook error: ${error instanceof Error ? error.message : error}`, {}, error instanceof Error ? error : undefined);View on GitHub (pinned to d8bc9755e7)