Yeachan-Heo/oh-my-codex · error · Error

question input must be a JSON object

Error message

question input must be a JSON object

What it means

normalizeQuestionInput requires the top-level question input to be a plain JSON object. Arrays, strings, numbers, null, or primitives are rejected because the normalizer expects an object with fields like header, source, questions, etc.

Source

Thrown at src/question/types.ts:185

  return {
    id,
    ...(header ? { header } : {}),
    question,
    options: rawOptions.map((option, optionIndex) => normalizeOption(option, optionIndex)),
    allow_other,
    other_label,
    multi_select: type === 'multi-answerable',
    type,
  };
}

function normalizeLegacyQuestion(raw: Record<string, unknown>, header?: string): NormalizedQuestionItem {
  return normalizeQuestionItem({ ...raw, id: safeString(raw.id).trim() || 'q-1', header }, 0, header);
}

export function normalizeQuestionInput(raw: unknown): QuestionInput {
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
    throw new Error('question input must be a JSON object');
  }

  const input = raw as Record<string, unknown>;
  const header = safeString(input.header).trim() || undefined;
  const source = safeString(input.source).trim() || undefined;
  const session_id = safeString(input.session_id).trim() || undefined;
  const rawQuestions = Array.isArray(input.questions) ? input.questions : undefined;

  const questions = rawQuestions
    ? rawQuestions.map((item, index) => normalizeQuestionItem(item, index, header))
    : [normalizeLegacyQuestion(input, header)];

  if (questions.length === 0) throw new Error('questions must be a non-empty array');
  const seenIds = new Set<string>();
  for (const question of questions) {
    if (seenIds.has(question.id)) throw new Error(`questions id must be unique: ${question.id}`);
    seenIds.add(question.id);
  }

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Wrap arrays in an object: { questions: [...] } instead of a bare array
  2. Ensure you JSON.parse the payload before passing it
  3. Check the value is non-null and typeof === 'object' before calling

Example fix

// before
normalizeQuestionInput([{ id: 'q1' }]);
// after
normalizeQuestionInput({ questions: [{ id: 'q1' }] });
Defensive patterns

Strategy: type-guard

Validate before calling

if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new TypeError('expected a question object');

Type guard

const isQuestionObject = (v: unknown): v is Record<string, unknown> =>
  Boolean(v) && typeof v === 'object' && !Array.isArray(v);

Prevention

When it happens

Trigger: Calling normalizeQuestionInput (or an API that normalizes input, e.g. record builders) with a JSON array of questions, a JSON string, or a null/undefined value.

Common situations: Passing JSON.parse output that is an array of question objects; reading a file whose root is an array; passing a stringified JSON instead of a parsed object.

Related errors


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