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

  1. Verify the language code matches the @tesseract.js-data package name (e.g. 'eng', 'fra', 'chi_sim').
  2. 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.
  3. Check the fetchUrl printed in the error in a browser/curl to confirm the server and CORS headers.
  4. 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

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.