upstash/context7 · error

Unsafe skill name

Error message

Unsafe skill name: ${JSON.stringify(skillName)}

What it means

Thrown by assertSkillNameInRoot when the requested skill name fails isSafeSkillName (SAFE_NAME regex check — names must be simple, safe identifier-like strings). This is the first line of defense against path traversal via the skill name itself; a second check afterwards also ensures the resolved target is a direct child of the skills root.

Solutions

  1. Use a plain skill name matching the allowed pattern (letters/digits/dashes, e.g. "my-skill") with no slashes, dots-prefixes, or spaces
  2. If you have a path or URL, extract just the skill's basename before passing it
  3. Sanitize or validate user input in scripts before passing it as skillName
  4. Check what the SAFE_NAME regex in skill-name.ts accepts and conform the name to it

Example fix

// before
await install("../downloaded/skill-pack");
// after
await install("skill-pack");
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_NAME = /^[\w][\w.-]*$/; // mirror the library's SAFE_NAME
if (!SAFE_NAME.test(skillName)) throw new Error(`Refusing unsafe skill name: ${skillName}`);

Type guard

function isSafeSkillName(name: string): boolean {
  return typeof name === "string" && name.length > 0 && !/[^\w.-]/.test(name) && !name.startsWith(".");
}

Try / catch

try {
  await installSkill(skillsRoot, rawName);
} catch (e) {
  if (e.message.startsWith("Unsafe skill name")) {
    console.error(`Invalid skill name '${rawName}'. Use letters, digits, and dashes only.`);
  }
}

Prevention

When it happens

Trigger: Calling any skill operation whose skillName argument contains path separators, `..`, leading dots, spaces, or other characters rejected by SAFE_NAME — e.g. `install("../evil")`, `install("my skill/v2")`, or an empty/oddly-cased name.

Common situations: User-typed skill names from CLI arguments containing typos, slashes, or shell-expanded paths; scripted installs interpolating untrusted input into the name; names copied from URLs that include version paths.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of upstash/context7@4416fb855b (2026-09-16). Data as JSON: /api/errors/028ce783c8d58c72. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/utils/skill-name.ts:16

import { resolve, dirname, basename } from "path";

const SAFE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;

export function isSafeSkillName(name: string): boolean {
  if (typeof name !== "string") return false;
  if (name.length === 0 || name.length > 128) return false;
  if (name === "." || name === "..") return false;
  if (name.includes("\0")) return false;
  if (!SAFE_NAME.test(name)) return false;
  return true;
}

export function assertSkillNameInRoot(skillsRoot: string, skillName: string): string {
  if (!isSafeSkillName(skillName)) {
    throw new Error(`Unsafe skill name: ${JSON.stringify(skillName)}`);
  }
  const root = resolve(skillsRoot);
  const target = resolve(root, skillName);
  if (dirname(target) !== root || basename(target) !== skillName) {
    throw new Error(`Skill name "${skillName}" escapes the skills root`);
  }
  return target;
}

View on GitHub (pinned to 4416fb855b)