Yeachan-Heo/oh-my-codex · error

answers[${index}].answer.kind must be option, other, or mult

Error message

answers[${index}].answer.kind must be option, other, or multi

What it means

Thrown by normalizeSubmittedAnswers when validating an entry in the answers[] array of a question submission. Each answer object's answer.kind field must be the string 'option', 'other', or 'multi' (after safeString coercion); any other value (undefined, number, null, arbitrary string) fails. This enforces the discriminated answer shape before the submission is persisted.

Source

Thrown at src/question/events.ts:226

        : [];

  if (rawAnswers.length === 0) throw new Error('answer payload must include answer or answers[]');
  const validQuestionIds = new Set((record.questions ?? []).map((question) => question.id));
  if (validQuestionIds.size === 0) validQuestionIds.add('q-1');
  const seen = new Set<string>();

  return rawAnswers.map((entry, index) => {
    if (!entry || typeof entry !== 'object' || Array.isArray(entry)) throw new Error(`answers[${index}] must be an object`);
    const object = entry as Record<string, unknown>;
    const questionId = safeString(object.question_id) || (record.questions?.[index]?.id ?? (index === 0 ? 'q-1' : ''));
    if (!questionId || !validQuestionIds.has(questionId)) throw new Error(`answers[${index}].question_id is unknown for this question: ${questionId || '<missing>'}`);
    if (seen.has(questionId)) throw new Error(`answers question_id must be unique: ${questionId}`);
    seen.add(questionId);
    const answer = object.answer;
    if (!answer || typeof answer !== 'object' || Array.isArray(answer)) throw new Error(`answers[${index}].answer must be an object`);
    const answerObject = answer as Record<string, unknown>;
    const kind = safeString(answerObject.kind);
    if (!['option', 'other', 'multi'].includes(kind)) throw new Error(`answers[${index}].answer.kind must be option, other, or multi`);
    if (!Array.isArray(answerObject.selected_labels) || !Array.isArray(answerObject.selected_values)) {
      throw new Error(`answers[${index}].answer must include selected_labels[] and selected_values[]`);
    }
    return {
      question_id: questionId,
      index: Number.isInteger(object.index) ? object.index as number : index,
      answer: answerObject as unknown as QuestionAnswerEntry['answer'],
    };
  });
}

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Set answers[i].answer.kind to one of 'option', 'other', or 'multi' exactly (lowercase).
  2. Ensure kind is a string primitive, not a String object or a number; safeString only accepts real strings.
  3. Validate the whole payload against the expected QuestionAnswerEntry shape before calling result/normalizeSubmittedAnswers.

Example fix

// before
answers: [{ question_id: 'q1', answer: { text: 'hello' } }]

// after
answers: [{ question_id: 'q1', answer: { kind: 'other', selected_labels: ['Other'], selected_values: ['hello'] } }]
Defensive patterns

Strategy: validation

Validate before calling

function isValidAnswerKind(a: unknown): boolean {
  const kind = (a as any)?.kind;
  return typeof kind === 'string' && ['option','other','multi'].includes(kind);
}
const ok = answers.every((e, i) => isValidAnswerKind(e?.answer) || (console.error(`answers[${i}].answer.kind invalid`), false));

Type guard

function isQuestionAnswerKind(v: unknown): v is 'option' | 'other' | 'multi' {
  return v === 'option' || v === 'other' || v === 'multi';
}

Try / catch

try { await result({ answers }); } catch (e) { if (e instanceof Error && /answer\.kind must be option/.test(e.message)) { /* fix payload at index parsed from message */ } else throw e; }

Prevention

When it happens

Trigger: Calling the question result/submission API with answers[i].answer missing kind, or with kind set to something like 'text', 'choice', 42, or undefined. safeString coerces non-strings to a sentinel, so only exactly 'option' | 'other' | 'multi' pass.

Common situations: Client serializing its own answer shape (e.g. {answer: {value: ...}}) instead of the OMX answer schema; version drift where older clients used a different kind enum; hand-built JSON payloads in tests or scripts.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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