Description
gemini hooks migrate copies a Claude Code hook's timeout value straight through, but the two products measure it in different units, so an explicitly-set timeout is reduced by a factor of 1000.
In packages/cli/src/commands/hooks/migrate.ts:
if ("timeout" in hook && typeof hook["timeout"] === "number") {
migrated["timeout"] = hook["timeout"];
}
The units differ:
- Claude Code —
hooks[].hooks[].timeout is in seconds (default 60).
- Gemini CLI —
packages/core/src/hooks/hookRunner.ts treats it as milliseconds:
var DEFAULT_HOOK_TIMEOUT = 6e4;
...
const timeout = hookConfig.timeout ?? DEFAULT_HOOK_TIMEOUT;
timeoutHandle = setTimeout(
() => reject(new Error(`Hook timed out after ${timeout}ms`)),
timeout
);
So a migrated hook carrying "timeout": 30 — 30 seconds under Claude Code — becomes 30 milliseconds under Gemini. Almost any real hook command exceeds that, so it fails with Hook timed out after 30ms.
The defaults happen to agree (Claude 60 s, Gemini 60000 ms), which is why this only bites users who set timeout explicitly — and why it is easy to miss in testing.
Expected behaviour
migrateClaudeHook converts the unit, e.g. migrated["timeout"] = hook["timeout"] * 1000.
Steps to reproduce
- In
~/.claude/settings.json, define a hook with "timeout": 30.
- Run
gemini hooks migrate.
- Inspect the generated Gemini settings:
timeout is 30, not 30000.
- Trigger the hook — it fails with
Hook timed out after 30ms.
Version
gemini-cli 0.57.0 (installed via Homebrew). Verified against the shipped bundle: bundle/gemini-OYYGXMHL.js for the migrator, bundle/chunk-S3MXVTTY.js for hookRunner.
Description
gemini hooks migratecopies a Claude Code hook'stimeoutvalue straight through, but the two products measure it in different units, so an explicitly-set timeout is reduced by a factor of 1000.In
packages/cli/src/commands/hooks/migrate.ts:The units differ:
hooks[].hooks[].timeoutis in seconds (default 60).packages/core/src/hooks/hookRunner.tstreats it as milliseconds:So a migrated hook carrying
"timeout": 30— 30 seconds under Claude Code — becomes 30 milliseconds under Gemini. Almost any real hook command exceeds that, so it fails withHook timed out after 30ms.The defaults happen to agree (Claude 60 s, Gemini 60000 ms), which is why this only bites users who set
timeoutexplicitly — and why it is easy to miss in testing.Expected behaviour
migrateClaudeHookconverts the unit, e.g.migrated["timeout"] = hook["timeout"] * 1000.Steps to reproduce
~/.claude/settings.json, define a hook with"timeout": 30.gemini hooks migrate.timeoutis30, not30000.Hook timed out after 30ms.Version
gemini-cli0.57.0 (installed via Homebrew). Verified against the shipped bundle:bundle/gemini-OYYGXMHL.jsfor the migrator,bundle/chunk-S3MXVTTY.jsforhookRunner.