JuliusBrussee/caveman · error · Error

[extract-atlas] missing numeric const

Error message

[extract-atlas] missing numeric const ${name}

What it means

The atlas generation script `extract-atlas.mjs` reads other source files as text and pulls out numeric `export const` declarations via regex (`constNumber`). This error is thrown when the regex `export const NAME = <digits>;` does not match, meaning a required numeric constant is absent or its form changed.

Solutions

  1. Open the source file and restore the constant to plain `export const NAME = 12345;` form
  2. If the constant was legitimately changed, keep the literal numeric form so the regex still matches
  3. Update extract-atlas.mjs to read the new declaration form or parse the AST instead of regex
  4. Confirm the correct file contents are being read (check the pxpipeRoot/rel path feeding constNumber)

Example fix

// before
export const TILE_SIZE = 2 ** 4;
// after
export const TILE_SIZE = 16;
Defensive patterns

Strategy: validation

Validate before calling

function numericConstPresent(src, name) {
  return new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(\\d+)\\s*;`).test(src);
}
if (!numericConstPresent(src, "TILE_SIZE")) throw new Error("fix constant before running extract-atlas");

Try / catch

try {
  runAtlasExtract();
} catch (e) {
  if (String(e.message).includes("missing numeric const")) {
    const name = e.message.split("missing numeric const ")[1];
    console.error(`Add 'export const ${name} = <integer>;' back to the source`);
  } else throw e;
}

Prevention

When it happens

Trigger: Source file passed to constNumber no longer contains `export const ${name} = <integer>;` — constant renamed, converted to a computed/calculated value, changed to a float or string, or removed.

Common situations: A refactor changed `const TILE = 16;` to `const TILE = 2 ** 4;` or imported it from another module; someone added a type annotation or made the value non-integer; the wrong source file's text is passed in.

Related errors


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

Appendix: source

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

  console.error('usage: node public/engine/pixel/internal/atlasgen/extract-atlas.mjs <pxpipe-root>');
  process.exit(2);
}

const pxpipeRoot = process.argv[2] ? resolve(process.argv[2]) : '';
if (!pxpipeRoot) usage();

const repoRoot = resolve(new URL('../../../../..', import.meta.url).pathname);
const outDir = join(repoRoot, 'public/engine/pixel/assets');
mkdirSync(outDir, { recursive: true });

function readSource(rel) {
  return readFileSync(join(pxpipeRoot, rel), 'utf8');
}

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}`);
    }
  }
}

View on GitHub (pinned to 3ee70a1026)