mastra-ai/mastra · error

Skill with id ${id} not found

Error message

Skill with id ${id} not found

What it means

In-memory skills storage update() looks up the skill by id in its map and throws when it is absent. Like create, update targets an existing record only — there is no upsert semantics.

Source

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

        changedFields: Object.keys(snapshotConfig),
        changeMessage: 'Initial version',
      });
    } catch (error) {
      // Roll back the orphaned skill record
      this.db.skills.delete(skill.id);
      throw error;
    }

    // Return the thin record
    return this.deepCopyConfig(newConfig);
  }

  async update(input: StorageUpdateSkillInput): Promise<StorageSkillType> {
    const { id, ...updates } = input;

    const existingConfig = this.db.skills.get(id);
    if (!existingConfig) {
      throw new Error(`Skill with id ${id} not found`);
    }

    // Separate metadata fields from config fields
    const { authorId, visibility, activeVersionId, status, ...rawConfigFields } = updates;

    // Filter out undefined keys: callers may spread partial snapshots into
    // update() and rely on "omit = no change" semantics. Without this, an
    // undefined value would clobber the latest version's populated field
    // when spread into newConfig below.
    const configFields: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(rawConfigFields)) {
      if (value !== undefined) configFields[key] = value;
    }

    // Config field names from StorageSkillSnapshotType
    const configFieldNames = [
      'name',
      'description',

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Create the skill before updating, or switch to a persistent storage adapter if data must survive restarts.
  2. Check existence via a get/has call before updating and create if missing (upsert pattern).
  3. Verify the id against a list call; fix typos or stale ids.
  4. Re-seed the in-memory store at startup if updates depend on seeded data.

Example fix

// before
await skillsStorage.update({ id: 'deploy', status: 'active' }); // throws if absent
// after
if (!skillsDb.skills.has('deploy')) {
  await skillsStorage.create({ skill: { id: 'deploy', ...defaults } });
}
await skillsStorage.update({ id: 'deploy', status: 'active' });
Defensive patterns

Strategy: validation

Validate before calling

const existing = await storage.get({ id });
if (!existing) throw new Error(`skill ${id} not found; create before update`);

Type guard

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

Try / catch

try {
  return await storage.update(input);
} catch (e) {
  if (e instanceof Error && e.message === `Skill with id ${input.id} not found`) {
    return storage.create({ skill: { id: input.id, ...defaults, ...input } as StorageCreateSkillInput });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling update({ id, ... }) for an id never created in this store instance; updating on a fresh/restarted process where the in-memory data is gone; a typo'd or cross-environment id.

Common situations: In-memory store reset between dev-server restarts while the client still holds old ids; test isolation issues where setup didn't create the skill; id mismatch after data re-seed with new ids.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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