{"record":{"id":"e2fa69680dbf765e","repo":"linshenkx/prompt-optimizer","slug":"variables-index-position-is-missing-a-valid-o-e2fa69","errorCode":null,"errorMessage":"variables[${index}].position is missing a valid \"occurrence\" number.","messagePattern":"variables\\[(.+?)\\]\\.position is missing a valid \"occurrence\" number\\.","errorType":"validation","errorClass":"VariableExtractionParseError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/variable-extraction/service.ts","lineNumber":233,"sourceCode":"        throw new VariableExtractionParseError(`variables[${index}] is missing a valid \"name\" field.`);\n      }\n\n      if (typeof variable.value !== 'string') {\n        throw new VariableExtractionParseError(`variables[${index}] is missing a valid \"value\" field.`);\n      }\n\n      if (!variable.position || typeof variable.position !== 'object') {\n        throw new VariableExtractionParseError(`variables[${index}] is missing a valid \"position\" object.`);\n      }\n\n      if (typeof variable.position.originalText !== 'string') {\n        throw new VariableExtractionParseError(\n          `variables[${index}].position is missing a valid \"originalText\" field.`\n        );\n      }\n\n      if (typeof variable.position.occurrence !== 'number') {\n        throw new VariableExtractionParseError(\n          `variables[${index}].position is missing a valid \"occurrence\" number.`\n        );\n      }\n\n      if (typeof variable.reason !== 'string') {\n        throw new VariableExtractionParseError(`variables[${index}] is missing a valid \"reason\" field.`);\n      }\n\n      return {\n        name: variable.name.trim(),\n        value: variable.value,\n        position: {\n          originalText: variable.position.originalText,\n          occurrence: variable.position.occurrence,\n        },\n        reason: variable.reason,\n        category: variable.category ? String(variable.category) : undefined,\n      };","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/variable-extraction/service.ts#L215-L251","documentation":"normalizeExtractionResponse requires each variable's position to include a numeric occurrence field indicating which occurrence of originalText the variable points at. This error means occurrence is missing or not a number (string counts like \"2\" are rejected).","triggerScenarios":"variables[i].position = { originalText: \"foo\" } with no occurrence, or occurrence: \"1\" / null / true instead of a number.","commonSituations":"LLMs frequently return occurrence as a quoted string; schema drift where the field is renamed to 'index' or 'nth'; models omitting the field when there is only one occurrence.","solutions":["Coerce string occurrences to numbers before parsing if you control the payload","Fix the prompt/template to state occurrence must be a JSON number, not a string","Check whether the model renamed the field and remap it (e.g. position.index -> position.occurrence)","Log the raw response to confirm the exact offending shape"],"exampleFix":"// before\nawait service.parseExtractionResult(text);\n\n// after\nconst parsed = JSON.parse(text);\nfor (const v of parsed.variables ?? []) {\n  const occ = v.position?.occurrence ?? v.position?.index;\n  if (typeof occ === 'string' && /^\\d+$/.test(occ)) v.position.occurrence = Number(occ);\n  else if (typeof v.position?.index === 'number') v.position.occurrence = v.position.index;\n}\nawait service.parseExtractionResult(JSON.stringify(parsed));","handlingStrategy":"validation","validationCode":"for (const v of parsed.variables ?? []) { const occ = v.position?.occurrence ?? v.position?.index; if (typeof occ === 'string' && /^\\d+$/.test(occ)) v.position.occurrence = Number(occ); else if (typeof occ !== 'number') v.position.occurrence = 0; }","typeGuard":"const hasOccurrence = (v: any) => typeof v?.position?.occurrence === 'number';","tryCatchPattern":"catch (e) { if (e instanceof VariableExtractionParseError && e.message.includes('occurrence')) { /* coerce and retry parse */ } throw e; }","preventionTips":["Prompt should say occurrence is a zero- or one-based JSON NUMBER, never a quoted string","Coerce common LLM quirks (quoted numbers, alternate key names) before parsing"],"tags":["llm-output","schema-validation","variable-extraction","type-coercion"],"backgroundTag":"llm-response-schema-invalid","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}