{"record":{"id":"b5dafefa308ab6cb","repo":"paperclipai/paperclip","slug":"this-question-requires-an-answer","errorCode":null,"errorMessage":"This question requires an answer","messagePattern":"This question requires an answer","errorType":"validation","errorClass":"PhotonAnswerValidationError","httpStatus":null,"severity":"error","filePath":"server/src/services/photon/interactions.ts","lineNumber":86,"sourceCode":"  if (!match) return null;\n  return {\n    command: match[1].toLowerCase() as \"answer\" | \"submit\",\n    reference: match[2],\n    questionIndex: Number(match[3] ?? 1) - 1,\n    value: match[4]?.trim() ?? \"\",\n  };\n}\nexport function parsePhotonQuestionAnswer(\n  interaction: AskUserQuestionsInteraction,\n  questionIndex: number,\n  value: string,\n): AskUserQuestionsAnswer {\n  const question = interaction.payload.questions[questionIndex];\n  if (!question)\n    throw new PhotonAnswerValidationError(\"That question does not exist\");\n  if (value.toLowerCase() === \"skip\") {\n    if (question.required !== false)\n      throw new PhotonAnswerValidationError(\"This question requires an answer\");\n    return { questionId: question.id, optionIds: [] };\n  }\n  if (!value || value.length > 100_000)\n    throw new PhotonAnswerValidationError(\n      \"Enter a nonempty answer within the task’s length limit\",\n    );\n  const numbers =\n    /^\\d+(?:[ ,]+\\d+)*$/.test(value) &&\n    question.options.some((option) => !option.freeText)\n      ? value.split(/[ ,]+/).map(Number)\n      : null;\n  if (numbers) {\n    if (\n      new Set(numbers).size !== numbers.length ||\n      numbers.some((index) => index < 1 || index > question.options.length)\n    )\n      throw new PhotonAnswerValidationError(\n        \"Choose valid option numbers without duplicates\",","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/interactions.ts#L68-L104","documentation":"PhotonAnswerValidationError thrown when the answer value is exactly the string \"skip\" (case-insensitive) but the question is not optional — required is anything other than false. Skipping is only permitted for questions explicitly marked required: false.","triggerScenarios":"Answering a required ask_user_questions question with the literal text \"skip\" via parsePhotonQuestionAnswer/processPhotonResponse.","commonSituations":"Agent or user tries to dodge a required question by typing \"skip\"; a UI offers a skip button without checking question.required; copied answer text from an optional question applied to a required one.","solutions":["Provide an actual answer (option number or free text) instead of \"skip\"","Only send \"skip\" when question.required === false","If the question should be skippable, update the interaction payload to set required: false and republish"],"exampleFix":"// before\nawait answer(interaction, index, \"skip\");\n// after\nif (question.required !== false) throw new Error(\"answer required\");\nawait answer(interaction, index, \"skip\");","handlingStrategy":"validation","validationCode":"if (value.toLowerCase() === 'skip' && question.required !== false) throw new Error('this question requires an answer');","typeGuard":"const canSkip = (q: { required?: boolean }): boolean => q.required === false;","tryCatchPattern":"try { return parsePhotonQuestionAnswer(interaction, i, value); } catch (e) { if (e instanceof PhotonAnswerValidationError && e.message === 'This question requires an answer') return promptUserForAnswer(question); throw e; }","preventionTips":["Only offer skip for questions with required === false","Inspect question.required before rendering a skip control","Answer required questions with real content"],"tags":["validation","required-field","skip"],"backgroundTag":"empty-required-field","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}