{"record":{"id":"ec9b12172f7c7c89","repo":"linshenkx/prompt-optimizer","slug":"variables-index-is-missing-a-valid-reason-fi","errorCode":null,"errorMessage":"variables[${index}] is missing a valid \"reason\" field.","messagePattern":"variables\\[(.+?)\\] is missing a valid \"reason\" field\\.","errorType":"validation","errorClass":"VariableExtractionParseError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/variable-extraction/service.ts","lineNumber":239,"sourceCode":"\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      };\n    });\n\n    return {\n      variables,\n      summary: data.summary.trim(),\n    };","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/variable-extraction/service.ts#L221-L257","documentation":"Each extracted variable must carry a human-readable reason string explaining why it was chosen as a variable. normalizeExtractionResponse throws this when variables[i].reason is missing or not a string, since the reason is part of the service's return contract.","triggerScenarios":"variables[i] has no 'reason' key, or reason is a number/object/null (e.g. reason: 5 or reason: {\"why\": \"...\"}).","commonSituations":"Prompt template that never asked for a reason field; model abbreviates the response and drops optional-looking fields; older template versions with a different schema; max_tokens truncation cutting off trailing fields.","solutions":["Update the extraction template so every variable object explicitly includes a 'reason' string","Default-fill reason when pre-processing the response if it's unimportant to your flow","Verify the template 'variable-extraction' content in your template manager matches the expected schema","Increase max_tokens if responses are being truncated mid-object"],"exampleFix":"// before\nawait service.parseExtractionResult(text);\n\n// after\nconst parsed = JSON.parse(text);\nfor (const v of parsed.variables ?? []) {\n  if (typeof v.reason !== 'string') v.reason = 'auto-filled: extracted by model';\n}\nawait service.parseExtractionResult(JSON.stringify(parsed));","handlingStrategy":"validation","validationCode":"for (const v of parsed.variables ?? []) { if (typeof v.reason !== 'string') v.reason = ''; }","typeGuard":"const hasReason = (v: any) => typeof v?.reason === 'string';","tryCatchPattern":"catch (e) { if (e instanceof VariableExtractionParseError && e.message.includes('reason')) { /* default-fill and re-parse */ } throw e; }","preventionTips":["Require a short reason string per variable in the template","Treat optional-looking fields as mandatory in the prompt wording"],"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"}