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
- Check tb's registered names (or wrap addClass in a Set-membership check) before calling
- Create a new TypeBuilder instance for each rebuild instead of reusing one
- Guard registration with a Map so classes are only added once
- 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
- Recreate the TypeBuilder on each rebuild/hot-reload
- Keep one name registry for classes and enums
- Centralize builder setup in a single idempotent function
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
- Enum ${name} already exists
- Property ${name} already exists.
- Class with name {name} already exists
- Property already exists: {} in class {}
- Property ${name} not found.
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/3130e7d4bc6dccf6.
Report an issue: GitHub.