santifer/career-ops · error · Error
Image failed to decode within 10s (unreadable or corrupt…
Error message
Image failed to decode within 10s (unreadable or corrupt file?): ${inputPath} What it means
After loading the image into a headless Chromium page, convertImageToPdf() waits up to 10 seconds for the <img> to fully decode (img.complete with naturalWidth/naturalHeight > 0). If the wait times out, the file is presumed unreadable or corrupt and this error replaces the generic timeout.
Solutions
- Open the image in a viewer to confirm it is not corrupt; re-export or re-download it if broken.
- Verify the file size is > 0 and that the bytes match the extension (`file image.png`).
- Convert/re-encode the image with an image tool (`magick input output.png`) to normalize it, then retry.
Example fix
// before
await convertImageToPdf('truncated.png', 'out.pdf');
// after (guard first)
import { statSync } from 'node:fs';
if (statSync('fixed.png').size === 0) throw new Error('empty image');
await convertImageToPdf('fixed.png', 'out.pdf'); Defensive patterns
Strategy: try-catch
Validate before calling
import { statSync } from 'node:fs';
import { readFileSync } from 'node:fs';
const buf = readFileSync(p);
if (buf.length < 8 || !buf.subarray(0, 8).toString('hex').match(/^(89504e47|ffd8ff|47494638|52494646)/)) {
throw new Error(`${p} does not look like a valid image`);
} Try / catch
try {
await convertImageToPdf(input, output);
} catch (err) {
if (err.message.includes('failed to decode')) {
console.error(`Image ${input} is corrupt or unreadable; re-export it and retry.`);
} else throw err;
} Prevention
- Sanity-check image magic bytes and file size before conversion.
- Re-encode suspicious images with an image tool before batching.
- Don't convert files still being written by another process.
When it happens
Trigger: convertImageToPdf() given a corrupt/truncated image file, a zero-byte file, a file whose bytes do not match its extension, or a path pointing at something that loads but never decodes (e.g. an HTML file named .png).
Common situations: Interrupted downloads producing truncated files; images with wrong extensions; files locked or still being written by another process during conversion.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Could not determine the rendered PDF page count from its…
- ENOENT
- Playwright extraction failed or blocked, trying fallback…
- Playwright extraction failed or blocked, trying fallback…
- Unsupported image type
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/7dc81207805e47f5.
Report an issue: GitHub.
Appendix: source
Thrown at img-to-pdf.mjs:140
<body>
<img id="career-ops-img" src="data:${mimeType};base64,${base64}">
</body>
</html>`;
await mkdir(dirname(outputPath), { recursive: true });
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'load' });
try {
await page.waitForFunction(() => {
const img = document.getElementById('career-ops-img');
return !!img && img.complete && img.naturalWidth > 0 && img.naturalHeight > 0;
}, { timeout: 10000 });
} catch (err) {
throw new Error(`Image failed to decode within 10s (unreadable or corrupt file?): ${inputPath}`);
}
const { width, height } = await page.evaluate(() => {
const img = document.getElementById('career-ops-img');
return { width: img.naturalWidth, height: img.naturalHeight };
});
// 96 CSS px per inch is the standard browser/Playwright conversion —
// sizing the PDF page to the image's own dimensions means the image
// fills the page exactly: no cropping, no blank margins.
const widthIn = width / 96;
const heightIn = height / 96;
const pdfBuffer = await page.pdf({
width: `${widthIn}in`,
height: `${heightIn}in`,
margin: { top: '0', right: '0', bottom: '0', left: '0' },
printBackground: true,View on GitHub (pinned to aac998c7ed)