paperclipai/paperclip · error · PhotonAnswerValidationError
This question requires an answer
Error message
This question requires an answer
What it means
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.
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
Example fix
// before
await answer(interaction, index, "skip");
// after
if (question.required !== false) throw new Error("answer required");
await answer(interaction, index, "skip"); Defensive patterns
Strategy: validation
Validate before calling
if (value.toLowerCase() === 'skip' && question.required !== false) throw new Error('this question requires an answer'); Type guard
const canSkip = (q: { required?: boolean }): boolean => q.required === false; Try / catch
try { return parsePhotonQuestionAnswer(interaction, i, value); } catch (e) { if (e instanceof PhotonAnswerValidationError && e.message === 'This question requires an answer') return promptUserForAnswer(question); throw e; } Prevention
- Only offer skip for questions with required === false
- Inspect question.required before rendering a skip control
- Answer required questions with real content
When it happens
Trigger: Answering a required ask_user_questions question with the literal text "skip" via parsePhotonQuestionAnswer/processPhotonResponse.
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- A full lowercase source SHA is required.
- A reusable lease cannot be replaced and reacquired in the…
- A reusable lease handoff requires an execution workspace…
- A safe, unique --revision is required
- A same-run reusable lease reacquisition requires a…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/b5dafefa308ab6cb.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/photon/interactions.ts:86
if (!match) return null;
return {
command: match[1].toLowerCase() as "answer" | "submit",
reference: match[2],
questionIndex: Number(match[3] ?? 1) - 1,
value: match[4]?.trim() ?? "",
};
}
export function parsePhotonQuestionAnswer(
interaction: AskUserQuestionsInteraction,
questionIndex: number,
value: string,
): AskUserQuestionsAnswer {
const question = interaction.payload.questions[questionIndex];
if (!question)
throw new PhotonAnswerValidationError("That question does not exist");
if (value.toLowerCase() === "skip") {
if (question.required !== false)
throw new PhotonAnswerValidationError("This question requires an answer");
return { questionId: question.id, optionIds: [] };
}
if (!value || value.length > 100_000)
throw new PhotonAnswerValidationError(
"Enter a nonempty answer within the task’s length limit",
);
const numbers =
/^\d+(?:[ ,]+\d+)*$/.test(value) &&
question.options.some((option) => !option.freeText)
? value.split(/[ ,]+/).map(Number)
: null;
if (numbers) {
if (
new Set(numbers).size !== numbers.length ||
numbers.some((index) => index < 1 || index > question.options.length)
)
throw new PhotonAnswerValidationError(
"Choose valid option numbers without duplicates",View on GitHub (pinned to 3f1d897a7c)