mastra-ai/mastra · error

${this.name}: version number ${input.versionNumber} already

Error message

${this.name}: version number ${input.versionNumber} already exists for entity ${parentId}

What it means

createVersion scans all stored versions and rejects a new version whose (parentId, versionNumber) pair matches an existing one. Even with unique ids, version numbers must be unique per entity so history ordering stays deterministic.

Source

Thrown at packages/core/src/storage/filesystem-versioned.ts:612

    };
  }

  // ==========================================================================
  // Version Methods (in-memory + git history)
  // ==========================================================================

  async createVersion(input: TVersion): Promise<TVersion> {
    await this.ensureGitHistory();
    if (this.versions.has(input.id)) {
      throw new Error(`${this.name}: version with id ${input.id} already exists`);
    }

    const parentId = (input as Record<string, unknown>)[this.parentIdField] as string;

    // Check for duplicate (parentId, versionNumber) pair
    for (const v of this.versions.values()) {
      if ((v as Record<string, unknown>)[this.parentIdField] === parentId && v.versionNumber === input.versionNumber) {
        throw new Error(`${this.name}: version number ${input.versionNumber} already exists for entity ${parentId}`);
      }
    }

    const version: TVersion = {
      ...input,
      createdAt: new Date(),
    } as TVersion;

    this.versions.set(input.id, structuredClone(version));
    return structuredClone(version);
  }

  async getVersion(id: string): Promise<TVersion | null> {
    await this.ensureGitHistory();
    return this.versions.has(id) ? structuredClone(this.versions.get(id)!) : null;
  }

  async getVersionByNumber(entityId: string, versionNumber: number): Promise<TVersion | null> {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Query listVersions for the entity and compute nextVersionNumber = max(existing) + 1 before creating.
  2. Serialize writes per entity (lock/queue) if multiple processes can create versions concurrently.
  3. Catch the error, re-read current max version number, and retry with the incremented value.

Example fix

// before
await store.createVersion({ id: vId, [parentIdField]: entityId, versionNumber: 3 });
// after
const { versions } = await store.listVersions({ [parentIdField]: entityId });
const next = Math.max(0, ...versions.map(v => v.versionNumber)) + 1;
await store.createVersion({ id: vId, [parentIdField]: entityId, versionNumber: next });
Defensive patterns

Strategy: validation

Validate before calling

const { versions } = await store.listVersions({ [parentIdField]: entityId });
const next = Math.max(0, ...versions.map(v => v.versionNumber)) + 1;
if (newVersionNumber !== next) throw new Error(`Expected versionNumber ${next}`);

Try / catch

try {
  await store.createVersion(version);
} catch (e) {
  if (String(e.message).includes('already exists for entity')) {
    // recompute next version number and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Creating two versions with the same versionNumber for the same parent entity; concurrent writers both assigning versionNumber N to the same entity; an import replaying versions of an entity.

Common situations: Manual version increments that didn't account for the latest stored version; optimistic client computing next versionNumber from stale data; parallel CI jobs writing to the same entity.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4046130205ff9e2d. Report an issue: GitHub.