Yeachan-Heo/oh-my-codex · critical · UltragoalError
Refusing a durable ultragoal mutation before writable lifecy
Error message
Refusing a durable ultragoal mutation before writable lifecycle authority is restored: ${error.message} Restore the authoritative session binding so OMX_SESSION_ID matches the current session.json before retrying; see docs/troubleshooting.md (stale session pointer recovery). What it means
A durable ultragoal mutation was blocked because assertUltragoalWritableLifecycleAuthority failed with either WRITABLE_STATE_SCOPE_ERRORS.unusableSession or unboundEnvironment. The wrapper converts these into an UltragoalError that explicitly refuses writes until the session binding is healed — the environment's OMX_SESSION_ID no longer matches the authoritative session.json, so writing now would corrupt ownership of durable state.
Source
Thrown at src/ultragoal/artifacts.ts:909
};
} catch (error) {
// Existing-plan mutators keep the documented unbound compatibility path.
// Bootstrap callers opt out so failed state activation cannot create a new durable plan.
if (
error instanceof Error
&& error.message === WRITABLE_STATE_SCOPE_ERRORS.unboundEnvironment
&& options.allowUnboundEnvironment !== false
) {
return { kind: 'no-pointer-compat' };
}
if (
error instanceof Error
&& (
error.message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession
|| error.message === WRITABLE_STATE_SCOPE_ERRORS.unboundEnvironment
)
) {
throw new UltragoalError(
`Refusing a durable ultragoal mutation before writable lifecycle authority is restored: ${error.message} Restore the authoritative session binding so OMX_SESSION_ID matches the current session.json before retrying; see docs/troubleshooting.md (stale session pointer recovery).`,
);
}
throw error;
}
}
function writableAuthorityEquals(
beforeLock: UltragoalWritableAuthority,
afterLock: UltragoalWritableAuthority,
): boolean {
if (beforeLock.kind !== afterLock.kind) return false;
if (beforeLock.kind === 'no-pointer-compat') return true;
const resolvedAfterLock = afterLock as Extract<UltragoalWritableAuthority, { kind: 'resolved' }>;
return beforeLock.source === resolvedAfterLock.source
&& beforeLock.sessionId === resolvedAfterLock.sessionId
&& beforeLock.stateDir === resolvedAfterLock.stateDir;
}View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Refresh the session binding so OMX_SESSION_ID equals the id in the current session.json (re-source the session env or restart from the launching shell)
- If the old session is the correct authority, restore session.json to point at it instead of exporting a new id
- Unset OMX_SESSION_ID if this context should be unbound-but-authorized, if your scope supports that mode
- See docs/troubleshooting.md, section 'stale session pointer recovery', as the message itself directs
Example fix
# before export OMX_SESSION_ID=old-session-id # stale omx ultragoal append --goal 'Fix::Thing' # refuses # after export OMX_SESSION_ID=$(jq -r .sessionId .omx/session.json) omx ultragoal append --goal 'Fix::Thing'
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
function sessionBindingIsCurrent(repoRoot: string): boolean {
const envId = process.env.OMX_SESSION_ID;
if (!envId) return false; // unboundEnvironment
try {
const session = JSON.parse(readFileSync(`${repoRoot}/.omx/session.json`, 'utf8'));
return session.sessionId === envId;
} catch {
return false;
}
} Type guard
function isLifecycleAuthorityError(e: unknown): boolean {
return e instanceof UltragoalError && e.message.includes('writable lifecycle authority is restored');
} Try / catch
try {
await mutateUltragoalState(...);
} catch (e) {
if (isLifecycleAuthorityError(e)) {
await refreshSessionBinding(); // re-export OMX_SESSION_ID from session.json, then retry once
return mutateUltragoalState(...);
}
throw e;
} Prevention
- Never hardcode or cache OMX_SESSION_ID across restarts; source it fresh from session.json
- Restart shells after session rotation instead of reusing old env
- Follow docs/troubleshooting.md stale session pointer recovery before retrying
When it happens
Trigger: Any durable ultragoal mutation API wrapped by this guard when OMX_SESSION_ID in the environment disagrees with the current session.json (unusableSession), or when the writable state scope has no session bound at all (unboundEnvironment) — e.g. reusing a shell from a previous session, resuming after a session restart, or exporting a stale OMX_SESSION_ID in CI.
Common situations: Long-lived terminal tabs where the session was rotated underneath; CI jobs caching environment variables between runs; scripts sourcing an old .env containing OMX_SESSION_ID; session.json rewritten by a new SessionStart while an old process still runs.
Related errors
- Refusing durable ultragoal mutation after writable lifecycle
- Cannot add a goal to an already completed aggregate ultragoa
- detached leader interrupted before release: ${interrupted}
- detached launch aborted before release
- Invalid --codex-goal-mode; expected aggregate or per-story.
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/3c9277d8cc9e77ce.
Report an issue: GitHub.