{"record":{"id":"dbc233990d3ec34e","repo":"Stirling-Tools/Stirling-PDF","slug":"conversion-did-not-return-json-content","errorCode":null,"errorMessage":"Conversion did not return JSON content","messagePattern":"Conversion did not return JSON content","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/tools/pdfTextEditor/PdfTextEditor.tsx","lineNumber":834,"sourceCode":"                data: isAxiosError(pollError)\n                  ? pollError.response?.data\n                  : undefined,\n                message:\n                  pollError instanceof Error ? pollError.message : undefined,\n              });\n              if (status === 404) {\n                throw new Error(\"Job not found on server\", {\n                  cause: pollError,\n                });\n              }\n            }\n          }\n\n          if (!jobComplete) {\n            throw new Error(\"Conversion timed out\");\n          }\n          if (!parsed) {\n            throw new Error(\"Conversion did not return JSON content\");\n          }\n        } else {\n          const content = await file.text();\n          const docResult = JSON.parse(content) as PdfJsonDocument;\n          parsed = {\n            ...docResult,\n            pages: docResult.pages ?? [],\n          };\n          shouldUseLazyMode = false;\n          pendingJobId = null;\n        }\n\n        setConversionProgress(null);\n\n        if (loadRequestIdRef.current !== requestId) {\n          return;\n        }\n","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/tools/pdfTextEditor/PdfTextEditor.tsx#L816-L852","documentation":"Thrown after the polling loop when jobComplete is true but parsed is still null. This is a defensive guard: the job completed, but the result retrieval/parsing block inside the loop did not execute or failed to set parsed. Logically, if jobComplete is true, the result fetch should have run — this throw catches a logic gap.","triggerScenarios":"jobStatus.complete was true but jobStatus.error was also truthy, and the error throw was caught by the catch block without setting jobComplete to false (a logic issue in the try/catch flow). Or the result fetch succeeded but JSON.parse threw, and the catch swallowed it.","commonSituations":"Edge case in the polling try/catch flow where a complete+error status is partially handled. JSON.parse failure on the result blob being caught by the pollError handler instead of being surfaced.","solutions":["Add logging before this throw to capture jobComplete=true, parsed=null, and the last jobStatus.","Review the try/catch boundary: the result fetch (lines 778-805) is inside the same try that catches poll errors — a JSON.parse failure there falls through to the catch without setting parsed.","Separate the result-fetch logic from the status-poll logic into distinct try/catch blocks.","If reproducible, report as a logic bug in the polling loop."],"exampleFix":"// before\nif (!parsed) {\n  throw new Error(\"Conversion did not return JSON content\");\n}\n\n// after\nif (!parsed) {\n  console.error(\"Job marked complete but no content parsed. Last status:\", lastJobStatusRef.current);\n  throw new Error(\"Conversion completed but result content was empty. Please try again.\");\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Separate result-fetch from status-poll to avoid catch-swallowing\nif (jobStatus.complete && !jobStatus.error) {\n  jobComplete = true;\n  try {\n    const resultResponse = await apiClient.get(`/api/v1/general/job/${jobId}/result`, {\n      responseType: 'blob',\n    });\n    const result = JSON.parse(await resultResponse.data.text());\n    if (Array.isArray(result.pages)) {\n      parsed = result;\n    }\n  } catch (resultError) {\n    console.error('Result fetch failed:', resultError);\n    // Don't let this be swallowed by the poll catch\n  }\n}","preventionTips":["Separate the result-fetch try/catch from the status-poll try/catch to prevent error swallowing.","Add logging before this defensive guard to capture all relevant state.","Review the polling loop flow for any path where jobComplete=true but parsed is never set.","Report as a logic bug if this throw is ever reached in production."],"tags":["pdf-text-editor","logic-error","conversion","defensive-guard","polling"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}