{"record":{"id":"fec478dfbf4266a0","repo":"CherryHQ/cherry-studio","slug":"invalid-response-format-from-dify-api-json-stri","errorCode":null,"errorMessage":"Invalid response format from Dify API: ${JSON.stringify(searchResponse)}","messagePattern":"Invalid response format from Dify API: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/difyKnowledge.ts","lineNumber":216,"sourceCode":"          retrieval_model: {\n            top_k: topK,\n            // will be error if not set\n            search_method: 'semantic_search',\n            reranking_enable: false,\n            score_threshold_enabled: false\n          }\n        })\n      })\n\n      if (!response.ok) {\n        const errorText = await response.text()\n        throw new Error(`API request failed, status code ${response.status}: ${errorText}`)\n      }\n\n      const searchResponse: DifySearchKnowledgeResponse = await response.json()\n\n      if (!searchResponse || !Array.isArray(searchResponse.records)) {\n        throw new Error(`Invalid response format from Dify API: ${JSON.stringify(searchResponse)}`)\n      }\n\n      const header = `### Query: ${query}\\n\\n`\n      let body: string\n\n      if (searchResponse.records.length === 0) {\n        body = 'No results found.'\n      } else {\n        const resultsText = searchResponse.records\n          .map((record, index) => {\n            const docName = record.segment.document?.name || 'Unknown Document'\n            const content = record.segment.content.trim()\n            const score = record.score\n            const keywords = record.segment.keywords || []\n\n            let resultEntry = `#### ${index + 1}. ${docName} (Relevant Score: ${(score * 100).toFixed(1)}%)`\n            resultEntry += `\\n${content}`\n            if (keywords.length > 0) {","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/difyKnowledge.ts#L198-L234","documentation":"Generic Error thrown by DifyKnowledgeServer.performSearchKnowledge when the parsed JSON response is null/falsy or does not contain a 'records' array. This guards against unexpected response shapes — the HTTP status was 200 but the body does not conform to the DifySearchKnowledgeResponse contract. The entire response is JSON-stringified into the error message for diagnosis.","triggerScenarios":"The Dify /retrieve endpoint returns 200 with a body that is null, an empty object, or has a different structure (e.g., an error object without the records field). This can happen with API version mismatches, non-standard Dify forks, or proxy/gateway responses that alter the body.","commonSituations":"Dify API version returns a different response schema; a reverse proxy injects a wrapper object; the endpoint returned an HTML error page that happened to parse as JSON; the Dify instance is a fork with a modified retrieve response.","solutions":["Log and inspect the full JSON.stringify(searchResponse) to identify the actual shape.","Verify the Dify API version matches the expected response contract.","If a proxy alters responses, configure it to pass through the Dify body unchanged.","Handle the malformed response gracefully with a user-friendly message."],"exampleFix":"// before\nif (!searchResponse || !Array.isArray(searchResponse.records)) {\n  throw new Error(`Invalid response format from Dify API: ${JSON.stringify(searchResponse)}`)\n}\n\n// after\nif (!searchResponse || !Array.isArray(searchResponse.records)) {\n  logger.error('Unexpected Dify retrieve response shape', { response: searchResponse })\n  return errorResult('Knowledge search returned an unexpected response format. Check Dify API version compatibility.')\n}","handlingStrategy":"type-guard","validationCode":"const searchResponse: unknown = await response.json()\nif (\n  typeof searchResponse !== 'object' ||\n  searchResponse === null ||\n  !Array.isArray((searchResponse as any).records)\n) {\n  logger.error('Unexpected Dify retrieve response', { body: searchResponse })\n  return { content: [{ type: 'text', text: 'Knowledge search returned an unexpected response format.' }], isError: true }\n}","typeGuard":"function isDifySearchResponse(res: unknown): res is { records: Array<{ segment: { content: string; document?: { name?: string }; keywords?: string[] }; score: number }> } {\n  return (\n    typeof res === 'object' &&\n    res !== null &&\n    'records' in res &&\n    Array.isArray((res as any).records)\n  )\n}","tryCatchPattern":"try {\n  return await performSearchKnowledge(id, query, topK, difyKey, apiHost)\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e)\n  if (msg.includes('Invalid response format')) {\n    // log full body and return a user-friendly error\n    return { content: [{ type: 'text', text: 'Unexpected API response. Check Dify version compatibility.' }], isError: true }\n  }\n  throw e\n}","preventionTips":["Log the full response body when the shape is unexpected to diagnose version mismatches.","Verify the Dify API version matches the expected response contract.","Use a runtime type guard before accessing .records to fail gracefully."],"tags":["dify","response-validation","schema-mismatch","api"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}