Yeachan-Heo/oh-my-codex · error · Error
failed to run notify-hook: ${result.error.message}
Error message
failed to run notify-hook: ${result.error.message} What it means
During `omx tmux-hook test`, the CLI spawns notify-hook.js with `node <hook> <json-payload>` via spawnSync. This error is the spawn-level failure (result.error), meaning Node could not launch the process at all — typically the executable path is wrong, permissions are denied, or resources are exhausted. It is distinct from error 483, which covers a launched hook that exited non-zero.
Source
Thrown at src/cli/tmux-hook.ts:474
const threadId = `tmux-test-${Date.now()}`;
const turnId = `turn-${Date.now()}`;
const message = args.join(' ').trim() || 'tmux-hook test payload';
const payload = {
type: 'agent-turn-complete',
cwd,
'thread-id': threadId,
'turn-id': turnId,
'input-messages': ['omx tmux-hook test'],
'last-assistant-message': message,
};
const result = spawnSync(process.execPath, [notifyHook, JSON.stringify(payload)], {
cwd,
encoding: 'utf-8',
windowsHide: true,
});
if (result.error) {
throw new Error(`failed to run notify-hook: ${result.error.message}`);
}
if (result.status !== 0) {
throw new Error(`notify-hook exited ${result.status}: ${(result.stderr || result.stdout || '').trim()}`);
}
console.log('tmux-hook test: notify-hook executed.');
console.log(`thread_id=${threadId}`);
console.log(`turn_id=${turnId}`);
console.log('Check: .omx/logs/tmux-hook-YYYY-MM-DD.jsonl for skip/reason codes.');
}
View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Confirm the file still exists at the printed path and re-run build if not (`npm run build`)
- Fix permissions: `chmod +x dist/scripts/notify-hook.js` (and ensure read access)
- Check for concurrent processes (file watchers, reinstall jobs) deleting dist/ during the test
- On EMFILE/ENOMEM, free file descriptors/memory or raise ulimit and retry
Example fix
// before omx tmux-hook test hello // Error: failed to run notify-hook: spawn ENOENT // after ls dist/scripts/notify-hook.js || npm run build chmod +x dist/scripts/notify-hook.js omx tmux-hook test hello
Defensive patterns
Strategy: fallback
Validate before calling
import { existsSync, accessSync, constants } from 'node:fs';
function canRunHook(hook: string): boolean {
try { accessSync(hook, constants.R_OK); return existsSync(hook); } catch { return false; }
} Try / catch
try { await testTmuxHook(args); } catch (e) { if (e instanceof Error && e.message.startsWith('failed to run notify-hook')) { await rebuildPackage(); await testTmuxHook(args); } else throw e; } Prevention
- Rebuild after pulling changes that touch scripts/
- Keep dist/ out of clean-on-save watcher tasks
- Verify file permissions in dockerized environments
When it happens
Trigger: spawnSync(process.execPath, [notifyHook, payload]) returns an error object: ENOENT if notifyHook path vanished between the existsSync check and spawn, EACCES if the file lost execute/read permission, or EMFILE/ENOMEM under resource pressure.
Common situations: File deleted or moved concurrently (watcher/reinstall mid-test); permission bits stripped by a copy/docker step; CI runners with low file-descriptor limits; antivirus quarantine on Windows.
Related errors
- [api] failed to launch native binary: executable is blocked
- failed to launch codex login: executable is blocked (${error
- --tmux-pane requires a pane id. ${SPARKSHELL_USAGE}
- --tail-lines requires a numeric value. ${SPARKSHELL_USAGE}
- --tail-lines must be an integer between 100 and 1000. ${SPAR
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/063d9eb4d3777763.
Report an issue: GitHub.