{"record":{"id":"5678be58c8c1314e","repo":"mozilla/pdf.js","slug":"no-valid-response-from-the-ai-service","errorCode":null,"errorMessage":"No valid response from the AI service.","messagePattern":"No valid response from the AI service\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/display/editor/stamp.js","lineNumber":192,"sourceCode":"      name: \"altText\",\n      request: {\n        data,\n        width,\n        height,\n        channels: data.length / (width * height),\n      },\n    });\n    if (!response) {\n      throw new Error(\"No response from the AI service.\");\n    }\n    if (response.error) {\n      throw new Error(\"Error from the AI service.\");\n    }\n    if (response.cancel) {\n      return null;\n    }\n    if (!response.output) {\n      throw new Error(\"No valid response from the AI service.\");\n    }\n    const altText = response.output;\n    await this.setGuessedAltText(altText);\n    if (updateAltTextData && !this.hasAltTextData()) {\n      this.altTextData = { alt: altText, decorative: false };\n    }\n    return altText;\n  }\n\n  #getBitmap() {\n    if (this.#bitmapId) {\n      this._uiManager.enableWaiting(true);\n      this._uiManager.imageManager\n        .getFromId(this.#bitmapId)\n        .then(data => this.#getBitmapFetched(data, /* fromId = */ true))\n        .finally(() => this.#getBitmapDone());\n      return;\n    }","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/display/editor/stamp.js#L174-L210","documentation":"Thrown by StampEditor.mlGuessAltText when the guess response is present, error-free, and not cancelled, but lacks an `output` string. It guards the final step that uses response.output as the alt text, so an empty/malformed success response is rejected rather than silently producing empty alt text.","triggerScenarios":"mlManager.guess resolves to an object with no error, no cancel, and a falsy `output` (e.g. { output: '' } or { } ).","commonSituations":"The model returned an empty string for an unrecognizable image; the backend schema changed and the text moved off the `output` key; a proxy trimmed the response body.","solutions":["Verify the ML backend returns { output: <non-empty string> } on success.","Ensure the alt-text model is the version expecting the {data,width,height,channels} request shape.","Catch the error and degrade to manual alt-text entry.","Log the full response object upstream of the throw to confirm the missing key."],"exampleFix":"// before\nconst alt = await editor.mlGuessAltText();\n// after\ntry {\n  const alt = await editor.mlGuessAltText();\n} catch (e) {\n  console.warn('Alt-text guess returned no usable output');\n}","handlingStrategy":"try-catch","validationCode":"// Cannot fully validate server output client-side; at least confirm the backend contract.\n// Ensure your mlManager normalizes responses to { output: string } on success.","typeGuard":"function isValidAltTextResponse(r) {\n  return !!r && !r.error && !r.cancel && typeof r.output === 'string' && r.output.length > 0;\n}","tryCatchPattern":"try {\n  const alt = await editor.mlGuessAltText();\n} catch (e) {\n  if (e.message === 'No valid response from the AI service.') {\n    // schema mismatch; fall back to manual entry\n  }\n}","preventionTips":["Pin the alt-text model version and its response schema.","Have your mlManager wrap/normalize responses to always include a non-empty output string.","Log the raw response upstream of the throw to catch schema drift early.","Never block editing on a successful ML guess; keep manual entry available."],"tags":["ai","alt-text","ml-manager","response-schema"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}