pbakaus/impeccable · error

font-match: no region {id}; ids: {ids}

Error message

font-match: no region {id}; ids: {ids}

What it means

After loading the spec, `font-match.mjs` looks up the requested id in the spec's `regions` array. If no region with that id exists, it prints this message including a comma-separated list of all valid region ids, so the error doubles as a discovery aid. Exit code is 1 and no font measurement happens.

Source

Thrown at crates/comp-verbs/src/font_match.rs:448

        io.err("usage: font-match.mjs --measure <text-region-id> | --rank <text-region-id> [--candidates \"Family:700,Family2:400,...\"] [--text \"...\"] [--transform uppercase] [--category sans,serif,display,handwriting,mono]\n");
        return 1;
    };
    let Some(spec_val) = spec.as_mut() else {
        io.err(&format!("font-match: no spec at {spec_path}; run comp-spec.mjs first\n"));
        return 1;
    };
    let region = spec_val
        .get("regions")
        .and_then(Value::as_array)
        .and_then(|a| a.iter().find(|r| r.get("id").and_then(Value::as_str) == Some(id)))
        .cloned();
    let Some(region) = region else {
        let ids = spec_val
            .get("regions")
            .and_then(Value::as_array)
            .map(|a| a.iter().filter_map(|r| r.get("id").and_then(Value::as_str)).collect::<Vec<_>>().join(", "))
            .unwrap_or_default();
        io.err(&format!("font-match: no region {id}; ids: {ids}\n"));
        return 1;
    };
    let comp_file = spec_val.get("comp").and_then(Value::as_str).unwrap_or("").to_string();
    let comp = match png_io::load_raster(&resolve(io, &comp_file)) {
        Ok((d, _)) => d.image,
        Err(e) => {
            io.err(&format!("font-match: cannot read {comp_file}: {e}\n"));
            return 1;
        }
    };
    let px = |k: &str| region.pointer(&format!("/px/{k}")).and_then(Value::as_f64).unwrap_or(0.0);
    let (rx, ry, rw, rh) = (px("x"), px("y"), px("w"), px("h"));
    let c = r::crop(&comp, rx, ry, rw, rh);
    let fp = fingerprint(&c, &FpOpts::default());
    let px_w = rw as i64;
    let px_h = rh as i64;
    let Some(fp) = fp else {
        // No lettering: record the attempt and size by the box.

View on GitHub (pinned to 2bc2879276)

Solutions

  1. Use an id from the `ids:` list printed in the error message.
  2. Inspect the spec's `regions` array (`jq '.regions[].id' <spec>`) to see current ids.
  3. Regenerate regions with `comp-spec --grid` / `--auto` if the region you need doesn't exist yet.
  4. Point `--spec` at the correct spec file if the id lives in a different one.

Example fix

// before
node font-match.mjs --measure hero
dev
// font-match: no region hero-title; ids: hero-title, nav-link
// after
node font-match.mjs --measure hero-title
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs';
const spec = JSON.parse(readFileSync(specPath, 'utf8'));
const ids = (spec.regions ?? []).map(r => r.id);
if (!ids.includes(regionId)) {
  throw new Error(`region "${regionId}" not in spec; available: ${ids.join(', ')}`);
}

Type guard

function regionExists(spec, id) {
  return Array.isArray(spec?.regions) && spec.regions.some(r => r?.id === id);
}

Try / catch

try {
  runFontMatch(['--measure', id]);
} catch (e) {
  if (String(e.stderr).startsWith('font-match: no region')) {
    const ids = String(e.stderr).match(/ids: (.*)/)?.[1] ?? '';
    console.warn(`Unknown id "${id}"; valid ids: ${ids}`);
  } else throw e;
}

Prevention

When it happens

Trigger: Passing `--measure <id>` or `--rank <id>` where `<id>` is misspelled, was renamed in the spec, or was never defined; the spec has an empty or missing `regions` array (ids list prints empty); the id belongs to a different spec file than the one loaded via `--spec`.

Common situations: Guessing region ids instead of reading the spec; editing region ids in the spec JSON by hand after running font-match scripts; stale automation pinned to an old id after a re-measure; the spec regenerated with `--auto` produced different band ids than expected.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08). Data as JSON: /api/errors/939515c9a19ee5df. Report an issue: GitHub.