Yeachan-Heo/oh-my-codex · error · Error
authority state cooldown_ms must be a non-negative number
Error message
authority state cooldown_ms must be a non-negative number
What it means
This error is thrown by validateAuthorityState in the HUD authority module when the persisted authority state file contains a cooldown_ms field that is not a finite non-negative number. The validator runs on every read of the authority state, so any corruption or hand-editing of the state file surfaces here. It protects the rate-limiting cooldown computation from NaN/negative values.
Source
Thrown at src/hud/authority.ts:154
function isAuthorityStatus(value: unknown): value is HudAuthorityState['last_status'] {
return value === 'spawned' || value === 'skipped' || value === 'failed' || value === 'locked';
}
function validateAuthorityState(value: unknown): HudAuthorityState {
if (typeof value !== 'object' || value === null) {
throw new Error('authority state must be an object');
}
const state = value as Partial<HudAuthorityState>;
if (state.owner !== 'hud') throw new Error('authority state owner must be hud');
if (typeof state.pid !== 'number' || !Number.isInteger(state.pid) || state.pid <= 0) {
throw new Error('authority state pid must be a positive integer');
}
if (typeof state.cwd !== 'string' || !state.cwd) throw new Error('authority state cwd must be a non-empty string');
if (parseIsoMs(state.heartbeat_at) === null) throw new Error('authority state heartbeat_at must be a valid ISO timestamp');
if (parseIsoMs(state.next_allowed_at) === null) throw new Error('authority state next_allowed_at must be a valid ISO timestamp');
if (typeof state.cooldown_ms !== 'number' || !Number.isFinite(state.cooldown_ms) || state.cooldown_ms < 0) {
throw new Error('authority state cooldown_ms must be a non-negative number');
}
if (typeof state.jitter_ms !== 'number' || !Number.isFinite(state.jitter_ms) || state.jitter_ms < 0) {
throw new Error('authority state jitter_ms must be a non-negative number');
}
if (typeof state.skip_count !== 'number' || !Number.isInteger(state.skip_count) || state.skip_count < 0) {
throw new Error('authority state skip_count must be a non-negative integer');
}
if (!isAuthorityStatus(state.last_status)) throw new Error('authority state last_status is invalid');
if (typeof state.last_reason !== 'string' || !state.last_reason) {
throw new Error('authority state last_reason must be a non-empty string');
}
return state as HudAuthorityState;
}
async function readAuthorityState(path: string): Promise<HudAuthorityState | null> {
try {
return validateAuthorityState(JSON.parse(await readFile(path, 'utf-8')));
} catch (error) {View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Inspect the authority state JSON file and fix cooldown_ms to a non-negative finite number (e.g. 0)
- Delete the authority state file so it is recreated with defaults
- Ensure only one version of the tool writes the state file (no version mixing)
- Check for concurrent writers racing on the state file
Example fix
// before: state file contains "cooldown_ms": "1000" // after: state file contains "cooldown_ms": 1000
Defensive patterns
Strategy: validation
Validate before calling
const s = JSON.parse(fs.readFileSync(authorityStatePath,'utf8'));
if (typeof s.cooldown_ms !== 'number' || !Number.isFinite(s.cooldown_ms) || s.cooldown_ms < 0) {
// repair or reset state before running the tick
} Type guard
function isValidAuthorityState(s: any): s is { cooldown_ms: number } {
return typeof s?.cooldown_ms === 'number' && Number.isFinite(s.cooldown_ms) && s.cooldown_ms >= 0;
} Try / catch
catch (e) { if ((e as Error).message.includes('cooldown_ms')) { /* reset state file and retry once */ } else throw e; } Prevention
- Never hand-edit authority state files
- Run one tool version against a given state directory
- Back up state before upgrades
When it happens
Trigger: readAuthorityState parses the authority state JSON and its cooldown_ms is a string, NaN, Infinity, a negative number, or missing/mis-typed after manual editing or a partially written file. Called from runHudAuthorityTick during tick processing.
Common situations: Hand-edited .omx state files, state written by an older version with a different schema, disk corruption or truncated JSON writes, or external tooling writing the state file with stringified numbers.
Related errors
- invalid auth slot path
- ${label} not found: ${path}
- Missing ${field}.
- Autoresearch goal ${mission.slug} cannot complete until prof
- autoresearch candidate artifact notes must be a string array
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/73c38ef34b6172f9.
Report an issue: GitHub.