{"record":{"id":"82a8b00c4dacb20d","repo":"chatboxai/chatbox","slug":"third-party-parser-failed","errorCode":null,"errorMessage":"third_party_parser_failed","messagePattern":"third_party_parser_failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/stores/sessionHelpers.ts","lineNumber":669,"sourceCode":"          break\n        }\n\n        case 'mineru': {\n          const apiToken = parserConfig.mineru?.apiToken\n          if (!apiToken) {\n            throw new Error('mineru_api_token_required')\n          }\n          try {\n            result = await parseFileWithMineruService(file, uniqKey, apiToken)\n          } catch (error) {\n            log.error(`MinerU parsing failed for \"${file.name}\":`, error)\n            if (\n              error instanceof Error &&\n              (error.message === EMPTY_ATTACHMENT_CONTENT_ERROR || error.message.startsWith('third_party_parser'))\n            ) {\n              throw error\n            }\n            throw new Error('third_party_parser_failed')\n          }\n          break\n        }\n\n        default: {\n          throw new Error('document_parser_not_configured')\n        }\n      }\n    }\n\n    stage = 'content_analysis'\n    const stats = getContentStats(result.content)\n    const sessionAttachmentWarningReason = isParsedContentVeryLarge(stats)\n      ? SESSION_ATTACHMENT_RAG_LARGE_ATTACHMENT_WARNING\n      : undefined\n    if (sessionAttachmentWarningReason) {\n      log.info(\n        `${SESSION_ATTACHMENT_RAG_LOG_PREFIX} Parsed content is very large: file=\"${file.name}\", parser=${result.parserType}, bytes=${stats.byteLength}, limit=${SESSION_ATTACHMENT_RAG_MAX_PARSED_BYTE_LENGTH}`","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/stores/sessionHelpers.ts#L651-L687","documentation":"Thrown in the 'mineru' catch block when parseFileWithMineruService rejects with an error whose message is NOT EMPTY_ATTACHMENT_CONTENT_ERROR and does NOT start with 'third_party_parser'. It is the generic MinerU failure bucket: network errors, HTTP 5xx, malformed MinerU responses, plugin exceptions, etc. The original error is logged via log.error first.","triggerScenarios":"parseFileWithMineruService throws something other than the two passthrough categories — e.g. a fetch rejection, a timeout, an unhandled exception inside platform.parseFileWithMineru, or a non-Error thrown value. The rethrow guard checks error.message prefix; anything else is collapsed into 'third_party_parser_failed'.","commonSituations":"MinerU API token expired/revoked (auth error); MinerU service down or returning 5xx; network proxy blocking the MinerU endpoint; desktop plugin crashed mid-parse; rate limited; the platform bridge threw an opaque Error whose message doesn't match the passthrough patterns.","solutions":["Check the renderer log line 'MinerU parsing failed for \"<file>\":' — it carries the original error; remediate the root cause (renew token, fix network, restart MinerU service).","Retry the attachment once for transient network/5xx failures; MinerU jobs are idempotent per file.","If the MinerU service is unavailable, switch the parser to 'local' or 'chatbox-ai' temporarily.","Improve error fidelity by extending the passthrough check (e.g. also forward auth/quota errors with their own codes) so users see the actual cause instead of the generic bucket."],"exampleFix":"// before\n} catch (error) {\n  log.error(`MinerU parsing failed for \"${file.name}\":`, error)\n  if (error instanceof Error &&\n      (error.message === EMPTY_ATTACHMENT_CONTENT_ERROR ||\n       error.message.startsWith('third_party_parser'))) {\n    throw error\n  }\n  throw new Error('third_party_parser_failed')\n}\n// after — preserve statusCode-bearing errors for the UI\n} catch (error) {\n  log.error(`MinerU parsing failed for \"${file.name}\":`, error)\n  if (error instanceof ApiError) throw error\n  if (error instanceof Error &&\n      (error.message === EMPTY_ATTACHMENT_CONTENT_ERROR ||\n       error.message.startsWith('third_party_parser'))) {\n    throw error\n  }\n  throw new Error('third_party_parser_failed')\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight: validate MinerU reachability with a cheap call before parsing a real file.\nasync function mineruReachable(apiToken: string): Promise<boolean> {\n  try { await platform.parseFileWithMineru!.healthCheck(apiToken); return true }\n  catch { return false }\n}","typeGuard":"function isThirdPartyParserFailed(e: unknown): boolean {\n  return e instanceof Error && e.message === 'third_party_parser_failed'\n}","tryCatchPattern":"try {\n  await preprocessFile(file)\n} catch (e) {\n  if (e instanceof Error && e.message === 'third_party_parser_failed') {\n    // check the renderer log for the wrapped cause; offer to switch parser\n    offerSwitchParser()\n    return\n  }\n  throw e\n}","preventionTips":["Always log the original error before collapsing into 'third_party_parser_failed' (the code already does this — keep the log line in Sentry breadcrumbs).","Forward statusCode-bearing errors verbatim so users see auth/quota messages instead of the generic bucket.","Provide a one-click 'switch to local parser' affordance when this error fires repeatedly."],"tags":["mineru","catch-all","network","plugin"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}