BoundaryML/baml · error · Error

Class ${name} already exists

Error message

Class ${name} already exists

What it means

TypeBuilder.addClass(name) refuses to add a class whose name is already registered in this JS-side TypeBuilder's class set, throwing this Error. It exists to prevent duplicate class definitions, which would be ambiguous when the builder is compiled into a BAML runtime.

Source

Thrown at engine/language_client_typescript/type_builder.js:69

    }
    union(types) {
        return this.tb.union(types);
    }
    classViewer(name, properties) {
        return new ClassViewer(this.tb, name, new Set(properties));
    }
    classBuilder(name, properties) {
        return new ClassBuilder(this.tb, name, new Set(properties));
    }
    enumViewer(name, values) {
        return new EnumViewer(this.tb, name, new Set(values));
    }
    enumBuilder(name, values) {
        return new EnumBuilder(this.tb, name, new Set(values));
    }
    addClass(name) {
        if (this.classes.has(name)) {
            throw new Error(`Class ${name} already exists`);
        }
        if (this.enums.has(name)) {
            throw new Error(`Enum ${name} already exists`);
        }
        this.classes.add(name);
        return new ClassBuilder(this.tb, name);
    }
    addEnum(name) {
        if (this.classes.has(name)) {
            throw new Error(`Class ${name} already exists`);
        }
        if (this.enums.has(name)) {
            throw new Error(`Enum ${name} already exists`);
        }
        this.enums.add(name);
        return new EnumBuilder(this.tb, name);
    }
    addBaml(baml) {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Check tb's registered names (or wrap addClass in a Set-membership check) before calling
  2. Create a new TypeBuilder instance for each rebuild instead of reusing one
  3. Guard registration with a Map so classes are only added once
  4. Wrap addClass in try/catch and ignore the already-exists error if idempotency is intended

Example fix

// before
const names = ["User", "User"];
names.forEach((n) => tb.addClass(n)); // throws on second
// after
const added = new Set();
names.forEach((n) => { if (!added.has(n)) { added.add(n); tb.addClass(n); } });
Defensive patterns

Strategy: validation

Validate before calling

// maintain own registry
const registered = new Set();
function addClassOnce(tb, name) { if (!registered.has(name)) { registered.add(name); tb.addClass(name); } }

Type guard

null

Try / catch

try { tb.addClass(name); } catch (e) {
  if (!String(e.message).startsWith('Class ')) throw e; // ignore duplicate-class only
}

Prevention

When it happens

Trigger: Calling tb.addClass("Foo") twice on the same TypeBuilder instance; rebuilding classes in a loop without a fresh TypeBuilder; a class and enum name collision handled separately (enums throw a different message).

Common situations: Hot-reload/dev loops that re-run type-builder setup code against a retained builder; initializing type builders per-request with module-level shared state; accidental duplicate registration from two setup functions.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/3130e7d4bc6dccf6. Report an issue: GitHub.