mastra-ai/mastra · error · HTTPException

Invalid skill name "${name}". Names must start with alphanum

Error message

Invalid skill name "${name}". Names must start with alphanumeric and contain only letters, numbers, hyphens, and underscores.

What it means

assertSafeSkillName validates skill names against SKILL_NAME_REGEX (/^[a-z0-9][a-z0-9-_]*$/i): must start with an alphanumeric and contain only letters, numbers, hyphens, and underscores. Anything else (spaces, dots, slashes, leading hyphen, unicode) triggers a 400.

Source

Thrown at packages/server/src/server/handlers/workspace.ts:1292

      }
      return handleError(error, 'Error fetching popular skills');
    }
  },
});

// =============================================================================
// Skills API helpers
// =============================================================================

/**
 * Validate skill name to prevent path traversal attacks.
 * Only allows alphanumeric characters, hyphens, and underscores.
 */
const SKILL_NAME_REGEX = /^[a-z0-9][a-z0-9-_]*$/i;

function assertSafeSkillName(name: string): string {
  if (!SKILL_NAME_REGEX.test(name)) {
    throw new HTTPException(400, {
      message: `Invalid skill name "${name}". Names must start with alphanumeric and contain only letters, numbers, hyphens, and underscores.`,
    });
  }
  return name;
}

/**
 * Validate that a file path is safe (no traversal, no absolute paths).
 * Prevents malicious API responses from writing files outside the skill directory.
 */
function assertSafeFilePath(filePath: string): string {
  // Reject absolute paths
  if (filePath.startsWith('/') || /^[a-zA-Z]:/.test(filePath)) {
    throw new HTTPException(400, {
      message: `Invalid file path "${filePath}". Absolute paths are not allowed.`,
    });
  }
  // Reject path traversal attempts

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Sanitize the name: lowercase, replace invalid characters with '-', strip leading non-alphanumerics.
  2. Trim file extensions before using a filename as a skill name.
  3. Validate on the client with the same regex before calling the API.
  4. If the name is inherently unsafe, use the ?path= disambiguation parameter instead of embedding it as a name.

Example fix

// before
const name = 'My Skill.v2';
// after
const name = 'My Skill.v2'.toLowerCase().replace(/[^a-z0-9-_]+/g, '-').replace(/^[^a-z0-9]+/, '');
Defensive patterns

Strategy: validation

Validate before calling

const SKILL_NAME_REGEX = /^[a-z0-9][a-z0-9-_]*$/i;
if (!SKILL_NAME_REGEX.test(name)) {
  throw new Error(`Skill name "${name}" is invalid; use [a-z0-9][a-z0-9-_]*`);
}

Type guard

function isValidSkillName(name: string): boolean {
  return /^[a-z0-9][a-z0-9-_]*$/i.test(name);
}

Try / catch

try {
  return await client.getSkillByName(name);
} catch (e) {
  if (isHttpException(e, 400) && String(e.message).includes('Invalid skill name')) {
    return await client.getSkillByName(sanitizeSkillName(name));
  }
  throw e;
}

Prevention

When it happens

Trigger: Any route that calls assertSafeSkillName with a name like 'my skill', '.hidden', 'skill.v2', '/etc/passwd', or a name starting with '-' or '_'.

Common situations: Deriving the name from a filename (e.g. 'README.md'); user-supplied names with spaces or dots; path fragments accidentally passed as names; non-ASCII names from other locales.

Related errors


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