mozilla/pdf.js · error · Error

coverage_search failed: ${result.error.message}

Error message

coverage_search failed: ${result.error.message}

What it means

resolveCodeTestIds() spawns `node external/ccov/coverage_search.mjs` via spawnSync to find tests covering the changed code. When `result.status` is non-zero AND `result.error` is set, Node itself could not launch the child process — the binary/script was missing, permissions denied, or spawn hit an OS error. The thrown message embeds `result.error.message` (e.g. ENOENT) to surface the real cause rather than a generic 'failed'. This is the spawn-failure branch of the coverage gating.

Source

Thrown at gulpfile.mjs:930

// For a --code=<file>::<line|function> argument, runs coverage_search to find
// the ref tests that exercise that location and returns their IDs (an empty
// array when none match locally). Returns null when --code wasn't given; throws
// when the search itself fails.
function resolveCodeTestIds() {
  const codeArg = getArgValue("--code");
  if (!codeArg) {
    return null;
  }
  // Inherit stderr so the index download progress is visible; stdout is
  // captured because it carries the matching test IDs.
  const result = spawnSync("node", getCoverageSearchArgs(codeArg), {
    encoding: "utf8",
    stdio: ["ignore", "pipe", "inherit"],
  });
  if (result.status !== 0) {
    // status is null when the child couldn't be spawned or was killed by a
    // signal; surface the real cause instead of a generic message.
    throw new Error(
      result.error
        ? `coverage_search failed: ${result.error.message}`
        : `coverage_search failed (exit code ${result.status})`
    );
  }
  let testIds = result.stdout.trim().split("\n").filter(Boolean);

  // The published index is built from master, so some covering tests may not
  // exist on this branch. Drop them (with a note) rather than letting test.mjs
  // reject the whole run — and silently exit 0 — on the first unknown ID.
  const knownIds = readManifestTestIds();
  if (knownIds) {
    const missing = testIds.filter(id => !knownIds.has(id));
    if (missing.length) {
      console.log(
        `\n### Ignoring ${missing.length} covered test(s) not in the manifest:\n` +
          missing.map(id => `  ${id}`).join("\n")
      );

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Verify `node` is installed and on PATH: `node --version`.
  2. Ensure `external/ccov/coverage_search.mjs` exists; fetch/redownload the ccov tooling if absent.
  3. Read `result.error.message` (printed in the thrown text) — ENOENT points to a missing binary/script, EACCES to permissions.
  4. If you don't need coverage selection, drop the `--code=<path>` argument so resolveCodeTestIds returns null and never spawns.
Defensive patterns

Strategy: try-catch

Validate before calling

import { existsSync } from 'fs';
function canSpawnCoverageSearch() {
  return existsSync('external/ccov/coverage_search.mjs') && !!process.execPath;
}

Try / catch

try {
  testIds = resolveCodeTestIds();
} catch (e) {
  if (/coverage_search failed/.test(e.message) && /ENOENT/.test(e.message)) {
    console.warn('coverage_search tooling missing; running full test set instead.');
    testIds = null;
  } else throw e;
}

Prevention

When it happens

Trigger: `node` is not on PATH in the build environment; `external/ccov/coverage_search.mjs` does not exist ( submodule not initialized, file deleted ); execute permission / shebang issues; EACCES on the working directory; spawning after the process table is exhausted.

Common situations: Fresh clone where the ccov data wasn't fetched; a container image missing node; a CI matrix that overrides PATH; running the coverage task on a branch that pre-dates coverage_search.mjs.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/3c648ba2727a3692. Report an issue: GitHub.