mastra-ai/mastra · error

Skill with id ${skill.id} already exists

Error message

Skill with id ${skill.id} already exists

What it means

The in-memory skills storage create() checks its skills map and rejects creation when a skill with the same id already exists. Ids are primary keys, so create is insert-only and duplicates are an error, not an overwrite.

Source

Thrown at packages/core/src/storage/domains/skills/inmemory.ts:51

  async dangerouslyClearAll(): Promise<void> {
    this.db.skills.clear();
    this.db.skillVersions.clear();
  }

  // ==========================================================================
  // Skill CRUD Methods
  // ==========================================================================

  async getById(id: string): Promise<StorageSkillType | null> {
    const config = this.db.skills.get(id);
    return config ? this.deepCopyConfig(config) : null;
  }

  async create(input: { skill: StorageCreateSkillInput }): Promise<StorageSkillType> {
    const { skill } = input;

    if (this.db.skills.has(skill.id)) {
      throw new Error(`Skill with id ${skill.id} already exists`);
    }

    const now = new Date();
    const visibility = skill.visibility ?? (skill.authorId ? 'private' : undefined);
    const newConfig: StorageSkillType = {
      id: skill.id,
      status: 'draft',
      activeVersionId: undefined,
      authorId: skill.authorId,
      visibility,
      favoriteCount: 0,
      createdAt: now,
      updatedAt: now,
    };

    this.db.skills.set(skill.id, newConfig);

    // Extract config fields from the flat input (everything except record fields)

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check existence first with this.db.skills.has(id) equivalent / a get call, then create or update accordingly.
  2. Make seeds idempotent: skip or update when the id already exists instead of always creating.
  3. Use unique ids per run (suffix with timestamp/uuid) when duplicates are expected.
  4. Clear the store between test runs.

Example fix

// before
await skillsStorage.create({ skill: { id: 'deploy', ...cfg } }); // throws on 2nd run
// after
const existing = await skillsStorage.get({ id: 'deploy' });
if (!existing) await skillsStorage.create({ skill: { id: 'deploy', ...cfg } });
else await skillsStorage.update({ id: 'deploy', ...cfg });
Defensive patterns

Strategy: validation

Validate before calling

const existing = await storage.get({ id: skill.id });
if (existing) throw new Error(`skill ${skill.id} already exists`);

Type guard

async function canCreate(storage: InMemorySkillsStorage, id: string): Promise<boolean> {
  return !(await storage.get({ id }));
}

Try / catch

try {
  return await storage.create({ skill });
} catch (e) {
  if (e instanceof Error && e.message.includes('already exists')) {
    return storage.update({ ...skill });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling create({ skill: { id: 'x', ... } }) twice for the same id, or via seedSkill re-running against an already-seeded store; re-running a seed/demo script on a warm process.

Common situations: Non-idempotent seed functions run on app startup; test setup reusing the same in-memory DB across cases; retrying a request after a timeout when the first attempt actually succeeded.

Related errors


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