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

Structured question returned no answer.

Error message

Structured question returned no answer.

What it means

primaryStructuredAnswer() extracts the answer from a structured question response by reading response.answers[0].answer, falling back to response.answer. If both are missing/empty, the guided autoresearch flow cannot proceed and throws.

Source

Thrown at src/cli/autoresearch-guided.ts:59

export interface AutoresearchStructuredQuestionInput {
	header?: string;
	question: string;
	options: Array<{ label: string; value: string; description?: string }>;
	allow_other: boolean;
	other_label?: string;
	multi_select?: boolean;
	type?: QuestionType;
	source?: string;
}

export type AutoresearchStructuredQuestionAsker = (
	input: AutoresearchStructuredQuestionInput,
) => Promise<OmxQuestionSuccessPayload>;

function primaryStructuredAnswer(response: OmxQuestionSuccessPayload): OmxQuestionSuccessPayload["answers"][number]["answer"] {
	const answer = response.answers[0]?.answer ?? response.answer;
	if (!answer) throw new Error("Structured question returned no answer.");
	return answer;
}

function createQuestionIO(): AutoresearchQuestionIO {
	const rl = createInterface({ input: process.stdin, output: process.stdout });
	return {
		question(prompt: string) {
			return rl.question(prompt);
		},
		close() {
			rl.close();
		},
	};
}

async function promptWithDefault(
	io: AutoresearchQuestionIO,
	prompt: string,

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the OmxQuestionSuccessPayload returned by the asker to see where the answer actually lives (answers[0].answer vs answer)
  2. If using a custom asker/mock, ensure it returns at least one entry in answers with a non-empty answer field
  3. In non-interactive automation, provide the answer directly instead of relying on the structured question path
  4. Wrap calls in try-catch and fall back to the plain readline io.question path

Example fix

// before
const asker = async () => ({ answers: [] });
// after
const asker = async () => ({ answers: [{ id: 'q1', answer: { value: 'launch' } }] });
Defensive patterns

Strategy: type-guard

Validate before calling

const resp = await asker(input);
if (!resp.answers?.[0]?.answer && !resp.answer) {
  throw new RetryableError('question backend returned no answer; retry or use interactive path');
}

Type guard

function hasStructuredAnswer(r: OmxQuestionSuccessPayload): boolean {
  return Boolean(r.answers?.[0]?.answer ?? r.answer);
}

Try / catch

try { return primaryStructuredAnswer(resp); } catch (e) { if (e.message === 'Structured question returned no answer.') return await io.question(promptText); throw e; }

Prevention

When it happens

Trigger: A structured question asker (the OMX question API invoked with createStructuredQuestionAsker) returns a success payload whose answers array is empty and which has no top-level answer field — e.g. a respondent skipped the question or a stub/mock asker returned an empty success object.

Common situations: Non-interactive contexts where the question backend returns an empty payload, mocks in tests returning {answers: []}, or upstream format changes where the answer lives under a different key.

Related errors


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