{"record":{"id":"413b7edb918717bc","repo":"paperclipai/paperclip","slug":"write-your-custom-answer-as-text","errorCode":null,"errorMessage":"Write your custom answer as text","messagePattern":"Write your custom answer as text","errorType":"validation","errorClass":"PhotonAnswerValidationError","httpStatus":null,"severity":"error","filePath":"server/src/services/photon/interactions.ts","lineNumber":109,"sourceCode":"      \"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\",\n      );\n    if (question.selectionMode === \"single\" && numbers.length !== 1)\n      throw new PhotonAnswerValidationError(\"Choose one option\");\n    if (numbers.some((index) => question.options[index - 1].freeText))\n      throw new PhotonAnswerValidationError(\"Write your custom answer as text\");\n    return {\n      questionId: question.id,\n      optionIds: numbers.map((index) => question.options[index - 1].id),\n    };\n  }\n  const freeText = question.options.find((option) => option.freeText);\n  if (question.options.length && question.allowOther === false && !freeText)\n    throw new PhotonAnswerValidationError(\n      \"Answer with the option number(s) shown in the prompt\",\n    );\n  return {\n    questionId: question.id,\n    optionIds: freeText ? [freeText.id] : [],\n    otherText: value,\n  };\n}\nexport function photonAnswersMatch(\n  interaction: AskUserQuestionsInteraction,","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/photon/interactions.ts#L91-L127","documentation":"PhotonAnswerValidationError thrown when the numeric answer selects an option whose freeText flag is true. Free-text options are not selectable by number — a custom answer must be submitted as plain text, not as an option index.","triggerScenarios":"Calling parsePhotonQuestionAnswer with a number pointing at the freeText option (e.g. the last \"Other…\" option) instead of providing the custom text directly.","commonSituations":"UI shows the free-text \"Other\" option numbered in the list and the user picks it by number; an agent enumerates all options including the freeText one.","solutions":["Submit the custom answer as plain text instead of the freeText option's number","Filter out freeText options when rendering selectable numbered options","Combine the option selection only over non-freeText options"],"exampleFix":"// before\nawait answer(interaction, index, String(question.options.length)); // the \"Other\" option\n// after\nawait answer(interaction, index, \"my custom answer\");","handlingStrategy":"validation","validationCode":"const selectable = question.options.map((o, idx) => ({ o, idx })).filter(x => !x.o.freeText); if (selectedNumbers.some(n => question.options[n-1]?.freeText)) throw new Error('free-text option not selectable by number');","typeGuard":"const isFreeTextOption = (o: { freeText?: boolean }) => o.freeText === true;","tryCatchPattern":"try { return parsePhotonQuestionAnswer(interaction, i, value); } catch (e) { if (e instanceof PhotonAnswerValidationError && /custom answer as text/.test(e.message)) return submitFreeText(value); throw e; }","preventionTips":["Exclude freeText options from numbered selectable lists","Route 'Other' selections into a text input instead of an index","Submit custom answers as plain text"],"tags":["validation","free-text","option-selection"],"backgroundTag":"invalid-argument-value","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"}