{"record":{"id":"27e04469fab8712f","repo":"linshenkx/prompt-optimizer","slug":"optimized-text-must-be-a-string","errorCode":null,"errorMessage":"Optimized text must be a string","messagePattern":"Optimized text must be a string","errorType":"validation","errorClass":"CompareValidationError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/compare/service.ts","lineNumber":69,"sourceCode":"        throw error;\n      }\n      \n      const errorMessage = error instanceof Error ? error.message : String(error);\n      throw new CompareCalculationError(\n        `Text comparison calculation failed: ${errorMessage}`\n      );\n    }\n  }\n\n  /**\n   * 验证输入参数\n   */\n  private validateInput(original: string, optimized: string): void {\n    if (typeof original !== 'string') {\n      throw new CompareValidationError('Original text must be a string');\n    }\n    if (typeof optimized !== 'string') {\n      throw new CompareValidationError('Optimized text must be a string');\n    }\n  }\n\n  /**\n   * 执行文本对比 - 使用 jsdiff\n   */\n  private performTextComparison(\n    original: string,\n    optimized: string,\n    options: CompareOptions\n  ): TextFragment[] {\n    let diffResult: Change[];\n\n    // 根据配置处理文本预处理\n    let processedOriginal = original;\n    let processedOptimized = optimized;\n\n    if (options.ignoreWhitespace) {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/compare/service.ts#L51-L87","documentation":"Thrown by normalizeReferencePromptPreview after the model's text is parsed and constrained: JSON.stringify of the constrained object produced nothing or the literal 'null'. This means the parsed 'object' was not a serializable record (e.g. null, undefined, or JSON.stringify returned undefined for a non-object) even though earlier parsing appeared to succeed.","triggerScenarios":"Calling resolveReferencePromptPreview where the model returns JSON like 'null', a bare string/number that parsed but wasn't a record, or an object whose serialization fails; constrainPromptVariables then yields null.","commonSituations":"Model outputs 'null' despite responseMimeType json; model wraps JSON in prose so extraction grabs the wrong fragment; upstream parse produced a non-record value; prompt changes causing the model to answer with a scalar.","solutions":["Strengthen the extraction prompt to demand a JSON object with specific keys","Add an explicit isRecord check right after JSON.parse so failures surface earlier with clearer intent","Retry the request; add 'Return only a JSON object, never null' to the system prompt","Validate model output against a zod/schema before normalizing"],"exampleFix":"// before\nconst formattedPrompt = JSON.stringify(constrainedPromptObject, null, 2)\nif (!formattedPrompt || formattedPrompt === 'null') {\n  throw new Error('Model response is not a valid JSON prompt object')\n}\n\n// after (reject non-records before serialization)\nif (!isRecord(constrainedPromptObject)) {\n  throw new Error('Model response is not a valid JSON prompt object')\n}\nconst formattedPrompt = JSON.stringify(constrainedPromptObject, null, 2)","handlingStrategy":"retry","validationCode":"// Pre-check the raw model text parses to a record before normalizing\nfunction parsesToRecord(text: string): boolean {\n  try {\n    return isRecord(JSON.parse(text))\n  } catch {\n    return false\n  }\n}","typeGuard":"function isRecord(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n}","tryCatchPattern":"try {\n  const preview = await extractor.resolveReferencePromptPreview(/* ... */)\n} catch (error) {\n  if (error instanceof Error && error.message.includes('not a valid JSON prompt object')) {\n    return retryOnceOrElse(defaultPreview)\n  }\n  throw error\n}","preventionTips":["Keep responseMimeType 'application/json' set on model calls","Add an explicit 'respond with a JSON object, never null' instruction","Validate model output with a schema (e.g. zod) before downstream processing"],"tags":["structured-output","json-parsing","ai-model"],"backgroundTag":"invalid-model-json-output","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}