mastra-ai/mastra · error · HTTPException
Prompt block with id ${id} already exists
Error message
Prompt block with id ${id} already exists What it means
The create prompt-block route enforces unique IDs: before inserting, it calls `promptBlockStore.getById(id)` and throws a 409 Conflict if a record with that ID already exists. Unlike some stores, the HTTP create path does not silently upsert — a duplicate ID is a conflict the caller must resolve.
Source
Thrown at packages/server/src/server/handlers/stored-prompt-blocks.ts:187
const promptBlockStore = await storage.getStore('promptBlocks');
if (!promptBlockStore) {
throw new HTTPException(500, { message: 'Prompt blocks storage domain is not available' });
}
// Derive ID from name if not explicitly provided
const id = providedId || toSlug(name);
if (!id) {
throw new HTTPException(400, {
message: 'Could not derive prompt block ID from name. Please provide an explicit id.',
});
}
// Check if prompt block with this ID already exists
const existing = await promptBlockStore.getById(id);
if (existing) {
throw new HTTPException(409, { message: `Prompt block with id ${id} already exists` });
}
await promptBlockStore.create({
promptBlock: {
id,
authorId,
metadata: scopeStoredResourceMetadata(metadata, await getStoredResourceScope(mastra, requestContext)),
name,
description,
content,
rules,
requestContextSchema,
},
});
// Return the resolved prompt block (thin record + version config)
// Use draft status since newly created entities start as drafts
const resolved = await promptBlockStore.getByIdResolved(id, { status: 'draft' });View on GitHub (pinned to 75dd419e61)
Solutions
- Choose a different unique id (or name) for the new prompt block
- Check existence first via GET /stored/prompt-blocks/:id and use the update (PUT) route to modify the existing record
- Make import scripts idempotent by fetching/updating instead of always creating
- Generate ids with a unique suffix (e.g. slug + timestamp or short uuid)
Example fix
// before
await client.createStoredPromptBlock({ id: 'my-prompt', name: 'My Prompt' }) // 409 if exists
// after
const existing = await client.getStoredPromptBlock('my-prompt');
if (existing) {
await client.updateStoredPromptBlock('my-prompt', { /* changes */ });
} else {
await client.createStoredPromptBlock({ id: 'my-prompt', name: 'My Prompt' });
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await fetch(`${baseUrl}/api/stored/prompt-blocks/${id}`);
if (existing.ok) {
// conflict: update instead of create
} Try / catch
try {
await client.createStoredPromptBlock(body);
} catch (e) {
if (isHTTPException(e) && e.status === 409 && e.message.includes('already exists')) {
await client.updateStoredPromptBlock(body.id, body);
return;
}
throw e;
} Prevention
- Use GET /stored/prompt-blocks/:id before create in scripts
- Generate ids with unique suffixes for non-idempotent imports
- Remember slug collisions: 'My Block' and 'my-block' share an id
When it happens
Trigger: POST /stored/prompt-blocks with an explicit `id` (or a name slugifying to an id) that already matches an existing stored prompt block in the database.
Common situations: Re-running a seed/import script without idempotency; two team members creating a block with the same name; retrying a create after a timeout when the first request actually succeeded; name collisions after slugification ('My Block' and 'my block' share the slug 'my-block').
Related errors
- Skipped: Scorer ${filename} already exists at ${scorersPath}
- Agent with id ${id} already exists
- MCP client with id ${id} already exists
- Scorer definition with id ${id} already exists
- Skill with id ${id} already exists
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/386e62d19efef9ed.
Report an issue: GitHub.