RocketChat/Rocket.Chat · error · Error

Failed to fetch encrypted PDF: ${response.status}

Error message

Failed to fetch encrypted PDF: ${response.status}

What it means

Thrown by useOpenEncryptedPdf when the fetch() for an encrypted PDF attachment returns a non-ok HTTP status (response.ok === false). The message embeds response.status (e.g. 401, 403, 404, 500) so the developer can see why the media fetch failed. The fetch targets getURL(link) on the media host; non-2xx means the resource could not be retrieved.

Source

Thrown at apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.ts:59

		if (blobUrlRef.current) {
			URL.revokeObjectURL(blobUrlRef.current);
			blobUrlRef.current = undefined;
		}

		if (abortControllerRef.current) {
			abortControllerRef.current.abort();
		}

		const abortController = new AbortController();
		abortControllerRef.current = abortController;

		try {
			const response = await fetch(getURL(link), {
				signal: abortController.signal,
			});
			if (!response.ok) {
				throw new Error(`Failed to fetch encrypted PDF: ${response.status}`);
			}
			const blob = await response.blob();
			if (abortController.signal.aborted || abortControllerRef.current !== abortController) {
				return;
			}
			const blobUrl = URL.createObjectURL(blob);
			blobUrlRef.current = blobUrl;
			openDocumentViewer(blobUrl, format, title ?? '');
		} catch (error: any) {
			if (error.name !== 'AbortError') {
				console.error('Error opening preview of encrypted PDF', error);
				throw error;
			}
		}
	};

	return openEncryptedPdf;
};

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Read response.status from the message: 401/403 → auth/permission, 404 → missing attachment, 5xx → server/storage.
  2. For 401/403, verify the session token is sent with the media request and the user has access to the room/attachment.
  3. For 404, confirm the attachment still exists (it may have been purged).
  4. Ensure credentials are included in the fetch if the media host differs from the app host (sameSite/CORS).
  5. If the PDF exceeds pdfPreviewSizeLimitInBytes the hook downloads instead of previewing — confirm size handling is intended.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check access before fetching the encrypted PDF
if (!hasAtLeastOnePermission('preview-file', roomId)) {
  throw new Error('Permission denied');
}

Type guard

function isOkResponse(r: Response): boolean {
  return r.ok;
}

Try / catch

try {
  await openEncryptedPdf(link, title, size, format, openViewer);
} catch (e) {
  const status = (e as Error).message.match(/(\d+)$/)?.[1];
  if (status === '401' || status === '403') { redirectToLogin(); }
  else if (status === '404') { showAttachmentMissing(); }
}

Prevention

When it happens

Trigger: The encrypted PDF blob endpoint returned 401 (token expired/not supplied), 403 (no permission for the room/attachment), 404 (attachment deleted or wrong link), or 5xx (media store/S3 failure). The check `if (!response.ok)` fires before reading the blob and throws the templated message.

Common situations: E2E encryption key not available so the server rejects the fetch; the user lacks 'preview-file' permission; the attachment was deleted; the S3/gridfs backing store is unreachable; the auth cookie/token was not sent with the fetch (CORS or credentials config).


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/741a09bced30aa9a. Report an issue: GitHub.