naptha/tesseract.js · error · Error
Network error while fetching ${fetchUrl}. Response code: ${r
Error message
Network error while fetching ${fetchUrl}. Response code: ${resp.status} What it means
After a cache miss, loadLanguage builds fetchUrl as `${langPath}/${lang}.traineddata${gzip ? '.gz' : ''}` (defaulting langPath to the jsdelivr CDN) and fetches it. If the response is not ok (HTTP non-2xx), it throws with the status code. This reject propagates up through loadLanguage/initialize and rejects the createWorker or reinitialize promise.
Source
Thrown at src/worker-script/index.js:143
// If `langPath` if not explicitly set by the user, the jsdelivr CDN is used.
// Data supporting the Legacy model is only included if `lstmOnly` is not true.
// This saves a significant amount of data for the majority of users that use LSTM only.
const langPathDownload = langPath || (lstmOnly ? `https://cdn.jsdelivr.net/npm/@tesseract.js-data/${lang}/4.0.0_best_int` : `https://cdn.jsdelivr.net/npm/@tesseract.js-data/${lang}/4.0.0`);
// For Node.js, langPath may be a URL or local file path
// The is-url package is used to tell the difference
// For the browser version, langPath is assumed to be a URL
if (env !== 'node' || isURL(langPathDownload) || langPathDownload.startsWith('moz-extension://') || langPathDownload.startsWith('chrome-extension://') || langPathDownload.startsWith('file://')) { /** When langPathDownload is an URL */
path = langPathDownload.replace(/\/$/, '');
}
// langPathDownload is a URL, fetch from server
if (path !== null) {
const fetchUrl = `${path}/${lang}.traineddata${gzip ? '.gz' : ''}`;
const resp = await (env === 'webworker' ? fetch : adapter.fetch)(fetchUrl);
if (!resp.ok) {
throw Error(`Network error while fetching ${fetchUrl}. Response code: ${resp.status}`);
}
data = new Uint8Array(await resp.arrayBuffer());
// langPathDownload is a local file, read .traineddata from local filesystem
// (adapter.readCache is a generic file read function in Node.js version)
} else {
data = await adapter.readCache(`${langPathDownload}/${lang}.traineddata${gzip ? '.gz' : ''}`);
}
} else {
data = _lang.data; // eslint-disable-line
}
}
progress += 0.5 / langsArr.length;
if (res) res.progress({ workerId, status: statusText, progress });
// Check for gzip magic numbers (1F and 8B in hex)
const isGzip = (data[0] === 31 && data[1] === 139) || (data[1] === 31 && data[0] === 139);View on GitHub (pinned to a1ca80d9e3)
Solutions
- Verify the language code matches the @tesseract.js-data package name (e.g. 'eng', 'fra', 'chi_sim').
- For offline/air-gapped use, pass langs as objects with embedded data: [{ code: 'eng', data: Uint8Array }], or set langPath to a reachable server/local path.
- Check the fetchUrl printed in the error in a browser/curl to confirm the server and CORS headers.
- Retry transient CDN failures via cacheMethod: 'write' so a later successful download persists.
Example fix
// before
const worker = await createWorker('en'); // wrong code -> 404
// after
const worker = await createWorker('eng'); // correct ISO 639-3 code
// offline bundle
const eng = fs.readFileSync('./eng.traineddata');
const worker = await createWorker([{ code: 'eng', data: eng }]); Defensive patterns
Strategy: retry
Validate before calling
// Validate the language code before creating the worker.
const VALID_LANGS = new Set(['eng', 'fra', 'deu', 'spa', 'chi_sim', 'chi_tra', 'jpn', 'kor']);
function assertLangs(langs) {
const arr = typeof langs === 'string' ? langs.split('+') : langs.map((l) => (typeof l === 'string' ? l : l.code));
const bad = arr.filter((l) => !VALID_LANGS.has(l));
if (bad.length) throw new Error('Unknown language code(s): ' + bad.join(','));
}
assertLangs('eng+fra');
// For offline use, embed the data so no fetch happens:
const engData = fs.readFileSync('./traineddata/eng.traineddata');
const worker = await createWorker([{ code: 'eng', data: engData }]); Type guard
const isLangObject = (l) => typeof l === 'object' && l !== null && typeof l.code === 'string' && (l.data instanceof Uint8Array);
Try / catch
async function createWorkerWithRetry(langs, opts, retries = 2) {
for (let attempt = 0; ; attempt++) {
try {
return await createWorker(langs, OEM.LSTM_ONLY, opts);
} catch (e) {
if (/Network error while fetching/.test(e.message) && attempt < retries) {
await new Promise((r) => setTimeout(r, 500 * (attempt + 1)));
continue;
}
throw e;
}
}
} Prevention
- Use ISO 639-3 codes ('eng', not 'en'); verify the code exists under @tesseract.js-data on npm/jsdelivr.
- Bundle traineddata as embedded data for offline or unreliable-network deployments.
- Point langPath at a self-hosted mirror you control when the jsdelivr CDN is unavailable.
- Set cacheMethod: 'write' so a single successful download survives transient CDN outages.
When it happens
Trigger: Wrong language code (e.g. 'en' instead of 'eng') returning 404; offline or DNS failure returning 5xx/0; custom langPath pointing to a misconfigured server; CORS blocking in the browser producing status 0; corporate firewall or CDN rate-limiting.
Common situations: Typo in the lang code; air-gapped deployment with no CDN access; self-hosted mirror with wrong path; browser extension blocking jsdelivr; region where the CDN is blocked.
Related errors
AI-assisted analysis of naptha/tesseract.js@a1ca80d9e3 (2026-08-13).
Data as JSON: /api/errors/dc3a728d65bec33d.
Report an issue: GitHub.