{"record":{"id":"eb92983324838aa9","repo":"linshenkx/prompt-optimizer","slug":"variables-index-position-is-missing-a-valid-o","errorCode":null,"errorMessage":"variables[${index}].position is missing a valid \"originalText\" field.","messagePattern":"variables\\[(.+?)\\]\\.position is missing a valid \"originalText\" field\\.","errorType":"validation","errorClass":"VariableExtractionParseError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/variable-extraction/service.ts","lineNumber":227,"sourceCode":"      // 验证必需字段\n      if (!variable || typeof variable !== 'object') {\n        throw new VariableExtractionParseError(`variables[${index}] is not a valid object.`);\n      }\n\n      if (typeof variable.name !== 'string' || !variable.name.trim()) {\n        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: {","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/variable-extraction/service.ts#L209-L245","documentation":"Thrown by normalizeExtractionResponse when a variable's position object exists but lacks a string originalText field. originalText is needed to disambiguate which occurrence of a substring in the prompt the variable refers to, so the service refuses to normalize responses without it.","triggerScenarios":"parseExtractionResult receives variables[i].position = { occurrence: 1 } with no originalText, or originalText is a number/boolean instead of a string.","commonSituations":"LLM emits position: { occurrence: 2 } only; field renamed by the model (e.g. 'text' or 'original'); template version mismatch after upgrade; model outputs null for originalText.","solutions":["Log the raw response and confirm the position object's actual keys","Update the extraction prompt to require originalText exactly (include a one-shot example in the template)","Recover programmatically: if position.occurrence exists but originalText is missing, populate it from variable.value before parsing","Retry with a more capable model if the schema is persistently ignored"],"exampleFix":"// before\nawait service.parseExtractionResult(text);\n\n// after\n// repair missing originalText from the variable value before parsing\nconst parsed = JSON.parse(text);\nfor (const v of parsed.variables ?? []) {\n  if (v.position && typeof v.position.originalText !== 'string' && typeof v.value === 'string') {\n    v.position.originalText = v.value;\n  }\n}\nawait service.parseExtractionResult(JSON.stringify(parsed));","handlingStrategy":"validation","validationCode":"for (const v of parsed.variables ?? []) { if (typeof v.position?.originalText !== 'string') v.position.originalText = v.value ?? ''; }","typeGuard":"const hasOriginalText = (v: any) => typeof v?.position?.originalText === 'string';","tryCatchPattern":"catch (e) { if (e instanceof VariableExtractionParseError && e.message.includes('originalText')) return repairAndRetry(text); throw e; }","preventionTips":["State explicitly in the prompt that position.originalText must be the exact substring copied from the prompt","Never trust nested LLM fields without checking typeof"],"tags":["llm-output","schema-validation","variable-extraction","json"],"backgroundTag":"llm-response-schema-invalid","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}