oven-sh/bun · error · Error

Unknown preprocessor macro ${name}

Error message

Unknown preprocessor macro ${name}

What it means

The preprocessor rewrites every $-prefixed identifier in builtin JS to __intrinsic__<name> and then tries to interpret <name>( calls as known macros ($debug, $assert, $rust, $cpp, $newRustFunction, $newCppFunction, $isPromise*, $bindgenFn). Any other $-prefixed identifier followed by ( is treated as an unknown macro and fails the build.

Source

Thrown at src/codegen/replacements.ts:302

          "[" +
          inner.result
            .slice(1, -1)
            .replaceAll("'", '"')
            .replace(/,[\s\n]*$/s, "") +
          "]";
        args = JSON.parse(str);
      } catch {
        throw new Error(`Call is not known at bundle-time: '$${name}${inner.result}'`);
      }
      if (args.length != 2 || typeof args[0] !== "string" || typeof args[1] !== "string") {
        throw new Error(`$${name} takes two string arguments, but got '$${name}${inner.result}'`);
      }

      const id = registerNativeCall("bind", args[0], args[1], null);

      return [slice.slice(0, match.index) + "__intrinsic__lazy(" + id + ")", inner.rest, true];
    } else {
      throw new Error("Unknown preprocessor macro " + name);
    }
  }
  return [slice, rest, false];
}

/** Applies source code replacements as defined in `globalReplacements` */
export function applyGlobalReplacements(src: string) {
  let result = src;
  for (const replacement of globalReplacements) {
    result = result.replace(replacement.from, replacement.toRaw ?? replacement.to!.replaceAll("$", "__intrinsic__"));
  }
  return result;
}

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Rename the helper so it does not start with $ — the $ prefix is reserved for preprocessor macros in src/js.
  2. If it is a genuine new macro, add its name to `function_replacements` in src/codegen/replacements.ts and implement its expansion branch in applyReplacements.
  3. Search src/js for `\$${name}(` to find the offending call site the message names.

Example fix

// before
function $fmt(s) { return s.trim(); }
$fmt(x);

// after
function fmt(s) { return s.trim(); }
fmt(x);
Defensive patterns

Strategy: validation

Validate before calling

const knownMacros = new Set(["debug", "assert", "rust", "cpp", "newRustFunction", "newCppFunction", "isPromiseFulfilled", "isPromiseRejected", "isPromisePending", "bindgenFn"]);
for (const m of src.matchAll(/(?:^|[^a-zA-Z0-9_])\$([a-zA-Z0-9_]+)\s*\(/g)) {
  if (!knownMacros.has(m[1])) throw new Error(`$${m[1]} is not a preprocessor macro; rename it to not start with $`);
}

Prevention

When it happens

Trigger: Defining or calling a helper whose name starts with $ inside src/js (e.g. `function $fmt(...)` then `$fmt(x)`), or adding a new intended macro without registering it in `function_replacements` in replacements.ts.

Common situations: Using jQuery-style $ naming conventions in builtin code; adding a new bundle-time macro but forgetting the registry list.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/94f85271f8d316d8. Report an issue: GitHub.