mastra-ai/mastra · error · HTTPException

Could not derive skill ID from name. Please provide an expli

Error message

Could not derive skill ID from name. Please provide an explicit id.

What it means

This 400 is thrown when neither an explicit `id` was provided in the request body nor could `toSlug(name)` produce a non-empty ID, i.e. the handler cannot determine an identifier for the new stored skill. It guards against inserting records with empty/undefined IDs. The library asks the caller to supply the ID explicitly.

Source

Thrown at packages/server/src/server/handlers/stored-skills.ts:329

    visibility: bodyVisibility,
  }) => {
    try {
      const storage = mastra.getStorage();

      if (!storage) {
        throw new HTTPException(500, { message: 'Storage is not configured' });
      }

      const skillStore = await storage.getStore('skills');
      if (!skillStore) {
        throw new HTTPException(500, { message: 'Skills 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 skill ID from name. Please provide an explicit id.',
        });
      }

      // Check if skill with this ID already exists
      const existing = await skillStore.getById(id);
      if (existing) {
        throw new HTTPException(409, { message: `Skill with id ${id} already exists` });
      }

      // Force authorId from the authenticated caller; ignore any body-provided value.
      // No owner = always public (no auth / no user context).
      // With an owner, respect the client's choice, defaulting to 'private'.
      const authorId = getCallerAuthorId(requestContext) ?? undefined;
      const visibility: 'private' | 'public' = authorId ? (bodyVisibility ?? 'private') : 'public';

      // Derive references/scripts/assets path arrays from the files tree
      // so agents can discover them via skill_read even when only `files` is provided.

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Provide an explicit `id` in the request body (kebab-case string)
  2. Send a non-empty, meaningful `name` that can be slugified into an ID
  3. Validate name/id client-side before calling the API and reject empty payloads
  4. If generating IDs programmatically, fall back to a UUID/slug when the name is unusable

Example fix

// before
await fetch('/api/stored-skills', { method: 'POST', body: JSON.stringify({ name: '***' }) });
// after
await fetch('/api/stored-skills', { method: 'POST', body: JSON.stringify({ id: 'my-skill', name: 'My Skill' }) });
Defensive patterns

Strategy: validation

Validate before calling

function assertValidSkillInput(body: { id?: string; name?: string }) {
  const id = body.id ?? toSlug(body.name ?? '');
  if (!id) throw new Error('Provide an explicit id or a non-empty slugifiable name');
  return id;
}

Type guard

function hasDerivableId(b: { id?: string; name?: string }): b is { id: string; name?: string } {
  return typeof b.id === 'string' && b.id.length > 0;
}

Try / catch

try {
  await createStoredSkill(body);
} catch (e) {
  if (String((e as Error).message).includes('derive skill ID')) {
    // retry with an explicit id
    await createStoredSkill({ ...body, id: body.id ?? crypto.randomUUID() });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /api/stored-skills with an empty or missing `name` (e.g. `""` or whitespace-only) and no `id` field; a name made entirely of characters that `toSlug` strips out, yielding an empty string; omitting both fields from the JSON payload.

Common situations: Client form allows submitting an empty skill name; programmatic callers send `{}` or a name of only punctuation/emoji that slugifies to nothing; API migration where `id` was previously auto-generated server-side.

Related errors


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