{"record":{"id":"927966bedda3bebf","repo":"Yeachan-Heo/oh-my-codex","slug":"structured-question-returned-an-invalid-next-step","errorCode":null,"errorMessage":"Structured question returned an invalid next-step answer.","messagePattern":"Structured question returned an invalid next-step answer\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/autoresearch-guided.ts","lineNumber":142,"sourceCode":"\t\t\t\t\tvalue: \"launch\",\n\t\t\t\t\tdescription: \"Finalize artifacts and hand off to $autoresearch.\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Refine further\",\n\t\t\t\t\tvalue: \"refine\",\n\t\t\t\t\tdescription: \"Keep clarifying before launch.\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tallow_other: false,\n\t\t\tsource: \"deep-interview\",\n\t\t});\n\t\tconst answer = primaryStructuredAnswer(response);\n\t\tconst answerValue = typeof answer.value === \"string\"\n\t\t\t? answer.value.trim().toLowerCase()\n\t\t\t: \"\";\n\t\tif (answerValue === \"launch\") return \"launch\";\n\t\tif (answerValue === \"refine\") return \"refine\";\n\t\tthrow new Error('Structured question returned an invalid next-step answer.');\n\t}\n\n\tconst answer = (\n\t\tawait io.question(\n\t\t\t`\\nNext step [launch/refine further] (default: ${launchReady ? \"launch\" : \"refine further\"})\\n> `,\n\t\t)\n\t)\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (!answer) return launchReady ? \"launch\" : \"refine\";\n\tif (answer === \"launch\") return \"launch\";\n\tif (answer === \"refine further\" || answer === \"refine\" || answer === \"r\")\n\t\treturn \"refine\";\n\tthrow new Error('Please choose either \"launch\" or \"refine further\".');\n}\n\nfunction createStructuredQuestionAsker(\n\trepoRoot: string,","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/autoresearch-guided.ts#L124-L160","documentation":"After extracting a structured answer for the next-step prompt, promptAction() only accepts the lowercased values 'launch' or 'refine'. Any other string (or non-string answer.value) is invalid and throws before falling back to interactive input.","triggerScenarios":"The structured asker returns an answer whose trimmed/lowercased value is something else — e.g. 'launch!', 'yes', 'L', or answer.value being a number/object (typeof !== 'string' yields '').","commonSituations":"LLM-backed answerers returning free-form text like 'let us launch now', multi-select payloads, or non-string value types from a changed schema.","solutions":["Make the structured asker return exactly 'launch' or 'refine' as answer.value","If the answerer is an LLM prompt, constrain it (enum/single-choice) so it emits only those two tokens","Catch this error and fall back to interactive io.question (shouldFallbackFromStructuredQuestion-style logic)"],"exampleFix":"// before\nanswer: { value: 'launch when ready' }\n// after\nanswer: { value: 'launch' }","handlingStrategy":"fallback","validationCode":"const val = String(answer?.value ?? '').trim().toLowerCase();\nif (val !== 'launch' && val !== 'refine') {\n  // fall back to interactive prompt instead of throwing\n  return await io.question('Next step [launch/refine further]\\n> ');\n}","typeGuard":"const isValidAction = (v: unknown): v is 'launch'|'refine' =>\n  typeof v === 'string' && ['launch','refine'].includes(v.trim().toLowerCase());","tryCatchPattern":"try { return await promptActionStructured(resp); } catch (e) { if (e.message.includes('invalid next-step answer')) return await promptActionInteractive(io); throw e; }","preventionTips":["Constrain LLM answerers to enum outputs ('launch'|'refine')","Normalize answer values (trim/lowercase) before validation"],"tags":["structured-question","invalid-answer","autoresearch"],"backgroundTag":"invalid-user-choice","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}