CherryHQ/cherry-studio · error · Error

Invalid response format from Dify API: ${JSON.stringify(sear

Error message

Invalid response format from Dify API: ${JSON.stringify(searchResponse)}

What it means

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.

Source

Thrown at src/main/ai/mcp/servers/difyKnowledge.ts:216

          retrieval_model: {
            top_k: topK,
            // will be error if not set
            search_method: 'semantic_search',
            reranking_enable: false,
            score_threshold_enabled: false
          }
        })
      })

      if (!response.ok) {
        const errorText = await response.text()
        throw new Error(`API request failed, status code ${response.status}: ${errorText}`)
      }

      const searchResponse: DifySearchKnowledgeResponse = await response.json()

      if (!searchResponse || !Array.isArray(searchResponse.records)) {
        throw new Error(`Invalid response format from Dify API: ${JSON.stringify(searchResponse)}`)
      }

      const header = `### Query: ${query}\n\n`
      let body: string

      if (searchResponse.records.length === 0) {
        body = 'No results found.'
      } else {
        const resultsText = searchResponse.records
          .map((record, index) => {
            const docName = record.segment.document?.name || 'Unknown Document'
            const content = record.segment.content.trim()
            const score = record.score
            const keywords = record.segment.keywords || []

            let resultEntry = `#### ${index + 1}. ${docName} (Relevant Score: ${(score * 100).toFixed(1)}%)`
            resultEntry += `\n${content}`
            if (keywords.length > 0) {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Log and inspect the full JSON.stringify(searchResponse) to identify the actual shape.
  2. Verify the Dify API version matches the expected response contract.
  3. If a proxy alters responses, configure it to pass through the Dify body unchanged.
  4. Handle the malformed response gracefully with a user-friendly message.

Example fix

// before
if (!searchResponse || !Array.isArray(searchResponse.records)) {
  throw new Error(`Invalid response format from Dify API: ${JSON.stringify(searchResponse)}`)
}

// after
if (!searchResponse || !Array.isArray(searchResponse.records)) {
  logger.error('Unexpected Dify retrieve response shape', { response: searchResponse })
  return errorResult('Knowledge search returned an unexpected response format. Check Dify API version compatibility.')
}
Defensive patterns

Strategy: type-guard

Validate before calling

const searchResponse: unknown = await response.json()
if (
  typeof searchResponse !== 'object' ||
  searchResponse === null ||
  !Array.isArray((searchResponse as any).records)
) {
  logger.error('Unexpected Dify retrieve response', { body: searchResponse })
  return { content: [{ type: 'text', text: 'Knowledge search returned an unexpected response format.' }], isError: true }
}

Type guard

function isDifySearchResponse(res: unknown): res is { records: Array<{ segment: { content: string; document?: { name?: string }; keywords?: string[] }; score: number }> } {
  return (
    typeof res === 'object' &&
    res !== null &&
    'records' in res &&
    Array.isArray((res as any).records)
  )
}

Try / catch

try {
  return await performSearchKnowledge(id, query, topK, difyKey, apiHost)
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e)
  if (msg.includes('Invalid response format')) {
    // log full body and return a user-friendly error
    return { content: [{ type: 'text', text: 'Unexpected API response. Check Dify version compatibility.' }], isError: true }
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/fec478dfbf4266a0. Report an issue: GitHub.