JuliusBrussee/caveman · error · Error

[extract-atlas] = , want

Error message

[extract-atlas] ${name}=${got}, want ${want}

What it means

`assertHeader` verifies that numeric header constants in a source file match expected values recorded by the atlas generator. When a constant's actual value differs from the expected one it throws with both values, signaling that the source and the atlas build are out of sync.

Solutions

  1. Regenerate the atlas/binary blob so its recorded header matches the current source constants
  2. If the constant change is intentional, update the expected values passed to assertHeader in extract-atlas.mjs
  3. Revert the accidental edit to the constant reported in the message
  4. Add a check in CI that runs extract-atlas after any change to the constants file

Example fix

// before (source edited but expectations stale)
export const ATLAS_VERSION = 3; // assertHeader expects 2
// after: update expectations
assertHeader(src, { ATLAS_VERSION: 3, /* ... */ });
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from "node:fs";
const src = readFileSync(pipelineFile, "utf8");
for (const [name, want] of Object.entries(expected)) {
  const m = src.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(\\d+)\\s*;`));
  if (!m || Number(m[1]) !== want) throw new Error(`${name} drifted; regenerate atlas`);
}

Try / catch

try {
  runAtlasExtract();
} catch (e) {
  const m = String(e.message).match(/\[extract-atlas\] (\w+)=(\d+), want (\d+)/);
  if (m) {
    console.error(`${m[1]} changed ${m[3]} -> ${m[2]}; regenerate the atlas or update expectations`);
  } else throw e;
}

Prevention

When it happens

Trigger: A numeric const in the checked source was edited (dimensions, version, tile size changed) so constNumber returns a value different from the `want` in the expected-header table passed to assertHeader.

Common situations: Someone changed a sprite-sheet size constant without regenerating the atlas; two branches diverged (one bumped a version const); a manual edit of generated source; the expected map in extract-atlas.mjs wasn't updated alongside the source.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/225d04966125c1b2. Report an issue: GitHub.

Appendix: source

Thrown at engine/pixel/internal/atlasgen/extract-atlas.mjs:45

function constNumber(src, name) {
  const re = new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(\\d+)\\s*;`);
  const m = src.match(re);
  if (!m) throw new Error(`[extract-atlas] missing numeric const ${name}`);
  return Number(m[1]);
}

function constB64(src, name) {
  const re = new RegExp(`const\\s+${name}\\s*=\\s*"([^"]+)"\\s*;`);
  const m = src.match(re);
  if (!m) throw new Error(`[extract-atlas] missing base64 const ${name}`);
  return Buffer.from(m[1], 'base64');
}

function assertHeader(src, expected) {
  for (const [name, want] of Object.entries(expected)) {
    const got = constNumber(src, name);
    if (got !== want) {
      throw new Error(`[extract-atlas] ${name}=${got}, want ${want}`);
    }
  }
}

function writeBlob(name, bytes) {
  const out = join(outDir, name);
  writeFileSync(out, gzipSync(bytes, { level: 9 }));
  console.log(`${basename(out)} raw=${bytes.length} gzip=${readFileSync(out).length}`);
}

const atlas = readSource('src/core/atlas.ts');
const gray = readSource('src/core/atlas-gray.ts');
assertHeader(atlas, HEADER.atlas);
assertHeader(gray, HEADER.gray);

writeBlob('atlas-codepoints.bin.gz', constB64(atlas, 'CODEPOINTS_B64'));
writeBlob('atlas-offsets.bin.gz', constB64(atlas, 'OFFSETS_B64'));
writeBlob('atlas-wideflags.bin.gz', constB64(atlas, 'WIDE_FLAGS_B64'));

View on GitHub (pinned to 3ee70a1026)