pbakaus/impeccable · error
font-match: no spec at {spec_path}; run comp-spec.mjs first
Error message
font-match: no spec at {spec_path}; run comp-spec.mjs first
What it means
`font-match.mjs` operates on the JSON spec produced by `comp-spec`. Before doing any region work it mutably loads the spec from the resolved `--spec` path (default SPEC_PATH); if the file is missing or empty (`spec.as_mut()` is None), it emits this message and exits 1, explicitly telling you to run `comp-spec` first. It is an ordering/dependency guard between the two verbs.
Source
Thrown at crates/comp-verbs/src/font_match.rs:434
}
let _ = std::fs::write(out, util::json_pretty(spec));
}
// ---- CLI -------------------------------------------------------------------
/// `impeccable font-match --measure <id> | --rank <id> ...`
pub fn run(argv: &[String], io: &mut Io, renderer: &mut dyn FontRenderer) -> i32 {
let spec_path = arg_or(argv, "spec", SPEC_PATH).to_string();
let mut spec = load_spec(&resolve(io, &spec_path));
let measure_id = arg(argv, "measure");
let rank_id = arg(argv, "rank");
let id = measure_id.or(rank_id);
let Some(id) = id else {
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)) {View on GitHub (pinned to 2bc2879276)
Solutions
- Run `comp-spec` (with `--grid`/`--regions` or `--auto`) first to generate the spec file.
- Verify the spec path: pass `--spec <path>` explicitly or run from the directory containing the default spec file.
- Check the spec file exists and is non-empty valid JSON (`cat <spec-path>`).
- If comp-spec previously failed, fix its error (e.g. bad --regions input) so the spec actually gets written.
Example fix
// before node font-match.mjs --measure hero-title // font-match: no spec at comp-spec.json; run comp-spec.mjs first // after node comp-spec.mjs --comp shot.png --auto node font-match.mjs --measure hero-title
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, statSync } from 'node:fs';
const specPath = specArg ?? 'comp-spec.json';
if (!existsSync(specPath) || statSync(specPath).size === 0) {
throw new Error(`${specPath} missing or empty — run comp-spec first`);
} Type guard
function hasSpec(path) {
try { return existsSync(path) && statSync(path).size > 0; } catch { return false; }
} Try / catch
try {
runFontMatch(args);
} catch (e) {
if (String(e.stderr).includes('no spec at')) {
runCompSpec(); // generate spec, then retry once
runFontMatch(args);
} else throw e;
} Prevention
- Treat comp-spec as a hard prerequisite; generate the spec as part of any script that calls font-match.
- Run all comp/font-match commands from the same working directory or pass --spec explicitly.
- Add the spec file to your build artifacts list so cleans don't silently delete it mid-workflow.
When it happens
Trigger: Running `font-match.mjs --measure <id>` before `comp-spec` has ever been run in the directory; the spec file was deleted or moved; `--spec` points at a wrong path; the spec file exists but contains no JSON object (e.g. empty or `null`).
Common situations: Fresh checkout or new project folder where only font-match is invoked; a clean-build/clean script removed generated spec files; running font-match from a different working directory so the default relative spec path resolves elsewhere; a previous comp-spec run failed (see error 50) so the spec was never written.
Understand the failure class
Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.
Related errors
- build-phase: no spec at {SPEC_PATH}; run comp-spec.mjs first
- comp-spec: no spec at {spec_path}; run with --comp <png> --r
- comp-spec: no spec at {spec_path}
- concept-seed: --candidate-count must be an integer from 5 to
- Unknown ignore-rule flag: ${arg}
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/a9bfee616aace6c8.
Report an issue: GitHub.