mastra-ai/mastra · error · Error
Version number ${input.versionNumber} already exists for pro
Error message
Version number ${input.versionNumber} already exists for prompt block ${input.blockId} What it means
InMemoryPromptBlockStorage.createVersion() throws this when a version with the same (blockId, versionNumber) pair already exists in the promptBlockVersions store. The library enforces unique version numbers per prompt block so version history stays monotonic and addressable. The version id is checked separately just before this check.
Source
Thrown at packages/core/src/storage/domains/prompt-blocks/inmemory.ts:186
perPage: perPageForResponse,
hasMore: offset + perPage < clonedBlocks.length,
};
}
// ==========================================================================
// Prompt Block Version Methods
// ==========================================================================
async createVersion(input: CreatePromptBlockVersionInput): Promise<PromptBlockVersion> {
// Check if version with this ID already exists
if (this.db.promptBlockVersions.has(input.id)) {
throw new Error(`Version with id ${input.id} already exists`);
}
// Check for duplicate (blockId, versionNumber) pair
for (const version of this.db.promptBlockVersions.values()) {
if (version.blockId === input.blockId && version.versionNumber === input.versionNumber) {
throw new Error(`Version number ${input.versionNumber} already exists for prompt block ${input.blockId}`);
}
}
const version: PromptBlockVersion = {
...input,
createdAt: new Date(),
};
// Deep clone before storing
this.db.promptBlockVersions.set(input.id, this.deepCopyVersion(version));
return this.deepCopyVersion(version);
}
async getVersion(id: string): Promise<PromptBlockVersion | null> {
const version = this.db.promptBlockVersions.get(id);
return version ? this.deepCopyVersion(version) : null;
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Query listVersions(blockId) first and compute the next versionNumber as max(existing)+1 before calling createVersion.
- Generate a fresh version id (crypto.randomUUID()) so the id-uniqueness check never collides, and only reuse versionNumber for idempotent replays you intend to reject.
- Wrap createVersion in try-catch and treat this message as 'already applied' if the replay is intentional.
- Clear the in-memory store (or use a fresh instance) between test/seed runs.
Example fix
// before
await storage.promptBlocks.createVersion({ id, blockId, versionNumber: 2, content });
// after
const { versions } = await storage.promptBlocks.listVersions(blockId, { perPage: false });
const next = versions.reduce((m, v) => Math.max(m, v.versionNumber), 0) + 1;
await storage.promptBlocks.createVersion({ id: crypto.randomUUID(), blockId, versionNumber: next, content }); Defensive patterns
Strategy: validation
Validate before calling
const { versions } = await storage.promptBlocks.listVersions(blockId, { perPage: false });
if (versions.some(v => v.versionNumber === nextVersionNumber)) {
throw new Error(`versionNumber ${nextVersionNumber} already used for block ${blockId}`);
} Type guard
function versionNumberIsFree(versions: { versionNumber: number }[], n: number): boolean {
return !versions.some(v => v.versionNumber === n);
} Try / catch
try {
await storage.promptBlocks.createVersion(input);
} catch (e) {
if (e instanceof Error && e.message.includes('already exists for prompt block')) {
return; // idempotent replay: version already recorded
}
throw e;
} Prevention
- Always derive versionNumber from existing versions (max+1), never hardcode it.
- Use crypto.randomUUID() for version ids.
- Make seed/import scripts idempotent or guard them with an existence check.
- Use a fresh in-memory storage instance per test to avoid cross-test residue.
When it happens
Trigger: Calling storage.promptBlocks.createVersion({ id: newId, blockId: 'b1', versionNumber: 2, ... }) when a version with blockId 'b1' and versionNumber 2 was already created (e.g. by a prior createVersion call or a seed routine like seedAgent that calls create->createVersion internally).
Common situations: Re-running a seeding/import script without clearing the store; retrying a failed request after the first attempt actually succeeded; concurrent writers racing to insert the next version number; hardcoding versionNumber instead of computing max+1.
Related errors
- Schedule ${schedule.id} already exists
- Scorer definition with id ${scorerDefinition.id} already exi
- Version with id ${input.id} already exists
- Skill with id ${skill.id} already exists
- Agent with id ${agent.id} already exists
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c952d7153af0744b.
Report an issue: GitHub.