Yeachan-Heo/oh-my-codex · error

answers[ ].answer must select exactly one option

Error message

answers[${entry.index}].answer must select exactly one option

What it means

An option-kind answer must contain exactly one selected value and exactly one selected label. Zero selections or more than one throws this error.

Solutions

  1. Disable submit until exactly one option is chosen for option-kind answers
  2. Reset/clear selection state when the question changes so old selections don't accumulate

Example fix

// before
if (selection.length >= 1) submit({ kind: 'option', selected_values: selection });
// after
if (selection.length === 1) submit({ kind: 'option', selected_values: selection, selected_labels: labels });
else showError('Pick exactly one option');
Defensive patterns

Strategy: validation

Validate before calling

if (answer.kind === 'option' && (answer.selected_values.length !== 1 || answer.selected_labels.length !== 1)) throw new Error('pick exactly one');

Type guard

function exactlyOne(arr: unknown[]): boolean { return arr.length === 1; }

Try / catch

try { await submit(p, answers); } catch (e) { if (/must select exactly one option/.test((e as Error).message)) { blockSubmitUntilOneSelected(); return; } throw e; }

Prevention

When it happens

Trigger: kind 'option' with selected_values.length !== 1 or selected_labels.length !== 1, e.g. submitting with nothing selected, or two values stuffed into a single-select answer.

Common situations: Submit button enabled with no selection; single-select widget accidentally accumulating multiple selections; porting a multi-select UI onto an option-kind payload.

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/82435f34116c6ea9. Report an issue: GitHub.

Appendix: source

Thrown at src/question/state.ts:387

  if (answer.kind === 'other') {
    if (!question.allow_other) throw new Error(`answers[${entry.index}].answer.kind=other is not allowed for this question`);
    if (multi) throw new Error(`answers[${entry.index}].answer.kind=other is only valid for single-answerable questions`);
    if (!otherText) throw new Error(`answers[${entry.index}].answer.other_text must be a non-empty string`);
    if (selectedValues.length !== 1 || selectedValues[0] !== otherText || answer.value !== otherText) {
      throw new Error(`answers[${entry.index}].answer other value must match selected_values[0] and other_text`);
    }
    assertSelectedLabelsMatch(
      selectedLabels,
      [question.other_label],
      `answers[${entry.index}].answer.selected_labels`,
    );
    return;
  }

  if (answer.kind === 'option') {
    if (multi) throw new Error(`answers[${entry.index}].answer.kind=option is only valid for single-answerable questions`);
    if (selectedValues.length !== 1 || selectedLabels.length !== 1) {
      throw new Error(`answers[${entry.index}].answer must select exactly one option`);
    }
    const selectedValue = selectedValues[0]!;
    if (!optionValues.has(selectedValue)) {
      throw new Error(`answers[${entry.index}].answer selected value is not in the option schema: ${selectedValue}`);
    }
    if (answer.value !== selectedValue) {
      throw new Error(`answers[${entry.index}].answer.value must match selected_values[0]`);
    }
    assertSelectedLabelsMatch(
      selectedLabels,
      expectedSelectedLabelsForValues(question, selectedValues, undefined),
      `answers[${entry.index}].answer.selected_labels`,
    );
    return;
  }

  if (!multi) throw new Error(`answers[${entry.index}].answer.kind=multi is only valid for multi-answerable questions`);
  if (!Array.isArray(answer.value)) throw new Error(`answers[${entry.index}].answer.value must be an array for multi answers`);

View on GitHub (pinned to 3ad79a8a6f)