BoundaryML/baml · error · Error

Property ${name} already exists.

Error message

Property ${name} already exists.

What it means

ClassBuilder.addProperty throws when a property with the same name has already been added to the class. The builder tracks property names in a Set and rejects duplicates so the generated BAML class has unique fields.

Source

Thrown at engine/language_client_typescript/typescript_src/type_builder.ts:185

  property(name: string): ClassPropertyViewer {
    if (!this.properties.has(name)) {
      throw new Error(`Property ${name} not found.`)
    }
    return new ClassPropertyViewer()
  }
}

export class ClassBuilder<ClassName extends string, Properties extends string = string> extends ClassAst<
  ClassName,
  Properties
> {
  constructor(tb: _TypeBuilder, name: ClassName, properties: Set<Properties | string> = new Set()) {
    super(tb, name, properties)
  }

  addProperty<S extends string>(name: RestrictNot<ClassName, S, Properties>, type: FieldType): ClassPropertyBuilder {
    if (this.properties.has(name)) {
      throw new Error(`Property ${name} already exists.`)
    }
    this.properties.add(name)
    return new ClassPropertyBuilder(this.bldr.property(name).setType(type))
  }

  listProperties(): Array<[string, ClassPropertyBuilder]> {
    return (this.bldr.listProperties() as Array<[string, _ClassPropertyBuilder]>).map(
      ([name, prop]) => [name, new ClassPropertyBuilder(prop)],
    )
  }

  removeProperty(name: string): void {
    this.properties.delete(name)
    this.bldr.removeProperty(name)
  }

  reset(): void {
    this.bldr.reset()

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Skip addProperty when the name is already present (check your own registry or listProperties).
  2. Use the returned property builder to update the type instead of re-adding.
  3. Recreate the ClassBuilder if stale state accumulates across rebuilds.

Example fix

// before
classBuilder.addProperty('id', t.string())
classBuilder.addProperty('id', t.int()) // throws
// after
if (!addedProps.has('id')) { classBuilder.addProperty('id', t.string()); addedProps.add('id') }
Defensive patterns

Strategy: validation

Validate before calling

if (classBuilder.listProperties().some(([n]) => n === name)) return // already added
classBuilder.addProperty(name, type)

Try / catch

try {
  classBuilder.addProperty(name, type)
} catch (e) {
  if (e.message.includes('already exists')) return
  throw e
}

Prevention

When it happens

Trigger: Calling classBuilder.addProperty('title', ...) twice on the same ClassBuilder, or a loop mapping object keys to properties that runs twice (e.g. retry or merge path).

Common situations: Merging two JSON schemas that share field names; building a class once at module load and again in a hot-reload callback.

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/d24db87e6c8f32a0. Report an issue: GitHub.