Yeachan-Heo/oh-my-codex · error

Question record not found: ${recordPath}

Error message

Question record not found: ${recordPath}

What it means

updateQuestionRecord reads the question record file before applying an updater; if readQuestionRecord returns null (file missing, unreadable, or unparseable into a record) it throws this error. The API refuses to 'update' a record that does not exist, mirroring a read-modify-write on a missing file.

Source

Thrown at src/question/state.ts:129

  const recordPath = getQuestionRecordPath(cwd, questionId, sessionId);
  await writeQuestionRecord(recordPath, record);
  if (options.emitEvent) {
    await appendQuestionEvent(cwd, 'question-created', record, {
      recordPath,
      timeoutMs: options.timeoutMs,
      runId: options.runId,
      now,
    });
  }
  return { recordPath, record };
}

export async function updateQuestionRecord(
  recordPath: string,
  updater: (record: QuestionRecord) => QuestionRecord,
): Promise<QuestionRecord> {
  const current = await readQuestionRecord(recordPath);
  if (!current) throw new Error(`Question record not found: ${recordPath}`);
  const updated = updater(current);
  await writeQuestionRecord(recordPath, updated);
  return updated;
}

export async function markQuestionPrompting(
  recordPath: string,
  renderer: QuestionRendererState,
  options: { closeQuestionRenderer?: CloseQuestionRenderer } = {},
): Promise<QuestionRecord> {
  return await withQuestionSubmitLock(recordPath, async () => {
    let rendererToClose: QuestionRendererState | undefined;
    const updated = await updateQuestionRecord(recordPath, (record) => {
      if (isTerminalQuestionStatus(record.status)) {
        rendererToClose = renderer;
        return record;
      }
      return {

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Verify the record file exists and parses before calling update (readQuestionRecord !== null)
  2. Re-check lifecycle: ensure the record is created before any update call and not deleted by a concurrent cleanup job
  3. If racing with cleanup, serialize via the provided submit lock so cleanup cannot interleave
  4. Handle the error by recreating the record or surfacing 'question not found' to the user instead of retrying blindly

Example fix

// before
await updateQuestionRecord(path, (r) => ({ ...r, status: 'answered' }));
// after
if (!(await readQuestionRecord(path))) {
  throw new Error(`question ${path} no longer exists; it may have expired`);
}
await updateQuestionRecord(path, (r) => ({ ...r, status: 'answered' }));
Defensive patterns

Strategy: validation

Validate before calling

const current = await readQuestionRecord(recordPath);
if (!current) throw new Error('record missing — recreate or report expired question');

Try / catch

try { await updateQuestionRecord(p, fn); } catch (e) { if (/Question record not found/.test((e as Error).message)) { await handleExpiredQuestion(p); return; } throw e; }

Prevention

When it happens

Trigger: Calling updateQuestionRecord (or higher-level updated/markQuestionTerminalError) with a recordPath that was deleted, never created, or contains invalid JSON; concurrent deletion by another process between creation and update.

Common situations: Record file cleaned up by a sweeper/expiry while an answer was in flight; wrong path passed (typo, wrong session dir); crash after partial write leaving corrupt JSON; tests that mock the filesystem incompletely.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/1797ff9088ed923b. Report an issue: GitHub.