{"record":{"id":"69745d378eea0359","repo":"Yeachan-Heo/oh-my-codex","slug":"path-must-not-contain-duplicate-values-value","errorCode":null,"errorMessage":"${path} must not contain duplicate values: ${value}","messagePattern":"(.+?) must not contain duplicate values: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/question/state.ts","lineNumber":306,"sourceCode":"      if (currentOwner.trim() === ownerToken) {\n        await rm(lockDir, { recursive: true, force: true });\n      }\n    } catch {\n    }\n  }\n}\n\nfunction validateStringArray(value: unknown, path: string): string[] {\n  if (!Array.isArray(value) || !value.every((item) => typeof item === 'string' && item.trim().length > 0)) {\n    throw new Error(`${path} must be a non-empty string array`);\n  }\n  return value;\n}\n\nfunction assertNoDuplicateValues(values: string[], path: string): void {\n  const seen = new Set<string>();\n  for (const value of values) {\n    if (seen.has(value)) throw new Error(`${path} must not contain duplicate values: ${value}`);\n    seen.add(value);\n  }\n}\n\nfunction expectedSelectedLabelsForValues(\n  question: NormalizedQuestionItem,\n  selectedValues: string[],\n  otherText: string | undefined,\n): string[] {\n  const optionLabelsByValue = new Map(question.options.map((option) => [option.value, option.label]));\n  return selectedValues.map((value) => {\n    const optionLabel = optionLabelsByValue.get(value);\n    if (optionLabel) return optionLabel;\n    if (question.allow_other && otherText && value === otherText) return question.other_label;\n    throw new Error(`selected value is not in the option schema: ${value}`);\n  });\n}\n","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/question/state.ts#L288-L324","documentation":"assertNoDuplicateValues rejects selected_values arrays containing the same value more than once. Multi-select answers must contain distinct option values; duplicates indicate a client bug or double-toggle when assembling the answer.","triggerScenarios":"Submitting a multi answer whose selected_values contains the same option twice, e.g. ['a','b','a']; commonly from concatenating selections without deduping.","commonSituations":"Merging selected arrays from multiple UI components; toggle handlers that push instead of add/remove; replay/retry logic appending already-present values.","solutions":["Dedupe before submit: [...new Set(selected_values)]","Fix the toggle handler to remove a value when it is already selected","Add a unit test asserting deduped output from the selection store"],"exampleFix":"// before\nanswer.selected_values = [...currentSelections, newlyToggled];\n// after\nconst set = new Set(currentSelections);\nset.has(newlyToggled) ? set.delete(newlyToggled) : set.add(newlyToggled);\nanswer.selected_values = [...set];","handlingStrategy":"validation","validationCode":"const deduped = [...new Set(answer.selected_values)];\nif (deduped.length !== answer.selected_values.length) answer.selected_values = deduped;","typeGuard":"function hasNoDuplicates(values: string[]): boolean { return new Set(values).size === values.length; }","tryCatchPattern":"try { await submit(p, answers); } catch (e) { if (/must not contain duplicate values/.test((e as Error).message)) { answers[i].selected_values = [...new Set(answers[i].selected_values)]; return submit(p, answers); } throw e; }","preventionTips":["Use a Set as the source of truth for multi-select state","Toggle add/remove instead of append"],"tags":["validation","multi-select","duplicates"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}