{"record":{"id":"907abc24b586efb6","repo":"CherryHQ/cherry-studio","slug":"document-conversion-produced-no-text","errorCode":null,"errorMessage":"Document conversion produced no text","messagePattern":"Document conversion produced no text","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/cherryDocumentTools.ts","lineNumber":136,"sourceCode":"        inputSchema: toMcpInputSchema(toMarkdownInputSchema)\n      }\n    ]\n  }\n\n  handles(toolName: string): boolean {\n    return toolName === TO_MARKDOWN_TOOL_NAME\n  }\n\n  async call(args: unknown, signal: AbortSignal): Promise<CallToolResult> {\n    try {\n      const { path: sourcePath } = toMarkdownInputSchema.parse(args)\n      const source = await resolveDocumentSource(this.context, sourcePath)\n      throwIfAborted(signal)\n\n      const anydoc = await loadAnydocModule()\n      const format = anydoc.formatFromExtension(path.extname(source.filename)) ?? undefined\n      const markdown = (await anydoc.toMarkdownBytes(Buffer.from(source.data, 'base64'), format)).trim()\n      if (!markdown) throw new Error('Document conversion produced no text')\n      throwIfAborted(signal)\n\n      const outputDirectory = path.join(this.context.agentDataPath, 'tmp', 'to-markdown')\n      await mkdir(outputDirectory, { recursive: true })\n      await cleanupStaleOutputs(outputDirectory).catch((error) => {\n        logger.warn('Failed to clean stale document conversion outputs', error as Error)\n      })\n\n      const outputPath = path.join(outputDirectory, `${randomUUID()}.md`)\n      await writeFile(outputPath, markdown, { encoding: 'utf-8', flag: 'wx' })\n      const output = toMarkdownOutputSchema.parse({ path: outputPath, chars: markdown.length })\n      return { content: [{ type: 'text', text: JSON.stringify(output) }] }\n    } catch (error) {\n      if (signal.aborted || isAbortError(error)) throw error\n      const normalizedError = error instanceof Error ? error : new Error(String(error))\n      logger.error('cherry-tools document conversion failed', normalizedError)\n      return errorResult(normalizedError)\n    }","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/cherryDocumentTools.ts#L118-L154","documentation":"Generic Error thrown by CherryDocumentTools.call when the anydoc document-to-Markdown converter returns an empty or whitespace-only string after trimming. The input file was read and decoded, but the converter extracted no text content. This usually means the source document is a scanned PDF without OCR, an image-only file, an empty document, or an unsupported/corrupt format that silently yields no output.","triggerScenarios":"Calling the to_markdown tool on: an image-only or scanned PDF without a text layer; an empty or near-empty file; a password-protected PDF; a format anydoc cannot parse; a corrupt/truncated file.","commonSituations":"User uploads a scanned document expecting text extraction but OCR is not enabled; the file extension does not match its actual content; the file was truncated during upload/transfer; the format is unsupported by the anydoc library version installed.","solutions":["Verify the source file actually contains extractable text (open it in a viewer).","For scanned PDFs, run OCR first, then pass the OCR'd file to to_markdown.","Confirm the file is not empty or password-protected.","Check that the file extension matches the real format and that anydoc supports it."],"exampleFix":"// before\nconst markdown = (await anydoc.toMarkdownBytes(Buffer.from(source.data, 'base64'), format)).trim()\nif (!markdown) throw new Error('Document conversion produced no text')\n\n// after\nconst markdown = (await anydoc.toMarkdownBytes(Buffer.from(source.data, 'base64'), format)).trim()\nif (!markdown) {\n  return errorResult(new Error('No text could be extracted. If this is a scanned document, run OCR first.'))\n}","handlingStrategy":"validation","validationCode":"// Pre-check: ensure the file has extractable content before calling to_markdown\nconst buf = Buffer.from(source.data, 'base64')\nif (buf.length === 0) {\n  throw new Error('Source file is empty')\n}\n// For PDFs, check page count or text layer before conversion if possible","typeGuard":null,"tryCatchPattern":"try {\n  const result = await documentTools.call(args, signal)\n  if (result.isError) {\n    // check if the error message indicates empty conversion\n    const text = result.content[0]?.text ?? ''\n    if (text.includes('produced no text')) {\n      // suggest OCR for scanned documents\n    }\n  }\n  return result\n} catch (e) {\n  if (e instanceof Error && e.message.includes('produced no text')) {\n    // run OCR then retry, or inform the user\n  }\n  throw e\n}","preventionTips":["Verify the document contains a text layer before conversion (scanned PDFs need OCR).","Check the file is not empty or password-protected.","Ensure the file extension matches the actual format and anydoc supports it."],"tags":["document-conversion","anydoc","empty-output","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}