vercel/ai · error

Invalid Pi ${label} name: ${name}

Error message

Invalid Pi ${label} name: ${name}

What it means

safePiMetadataSegment validates names used in Pi metadata file paths such as `.pi/skills/<name>/` or `.pi/agents/<name>.md`. It only allows `[A-Za-z0-9._-]` and rejects '.' and '..' to prevent path traversal and shell-sensitive characters. Any name containing slashes, spaces, or other special characters causes this throw.

Source

Thrown at packages/harness-pi/src/pi-utils.ts:78

    return output;
  }
  const serialized = JSON.stringify(output);
  return serialized ?? 'null';
}

export function getErrorText(error: unknown): string {
  return error instanceof Error ? error.message : String(error);
}

/**
 * Validate that a name is safe to use as a filesystem path segment under
 * `.pi/skills/<name>/` or `.pi/agents/<name>.md`. Refuses anything that
 * could be interpreted as a path traversal or contains shell-sensitive
 * characters.
 */
export function safePiMetadataSegment(name: string, label: string): string {
  if (!/^[A-Za-z0-9._-]+$/.test(name) || name === '.' || name === '..') {
    throw new Error(`Invalid Pi ${label} name: ${name}`);
  }
  return name;
}

/** Frontmatter renderer for `.pi/skills/<name>/SKILL.md`. */
export function renderPiSkillFile(skill: HarnessV1Skill): string {
  return `---\nname: ${skill.name}\ndescription: ${skill.description}\n---\n\n${skill.content}`;
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Sanitize the name before passing it: lowercase, replace invalid characters with '-', trim.
  2. Derive the name from the last path segment and strip extensions/slashes.
  3. Catch the error and surface a clear validation message to the user configuring the skill/agent.
  4. If you control upstream naming, restrict IDs to the allowed charset at creation time.

Example fix

// before
renderPiSkillFile({ name: 'code-review/assistant', ... });
// after
const safeName = 'code-review/assistant'
  .split('/')
  .pop()!
  .replace(/[^A-Za-z0-9._-]+/g, '-')
  .replace(/^\.+$/, '');
renderPiSkillFile({ name: safeName, ... });
Defensive patterns

Strategy: validation

Validate before calling

const PI_NAME_RE = /^[A-Za-z0-9._-]+$/;
function validatePiName(name, label) {
  if (!PI_NAME_RE.test(name) || name === '.' || name === '..') {
    throw new Error(`Invalid Pi ${label} name: ${name}`);
  }
}
validatePiName(skill.name, 'skill');

Try / catch

try {
  renderPiSkillFile(skill);
} catch (err) {
  if (String(err.message).startsWith('Invalid Pi ')) {
    skill.name = slugify(skill.name);
    return renderPiSkillFile(skill);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling safePiMetadataSegment(name, label) — directly or via skill/agent rendering functions like renderPiSkillFile — with a name containing '/', '\', spaces, unicode, or equal to '.' or '..'.

Common situations: Deriving a skill name from a URL slug or file path (e.g. 'my/skill'); user-entered skill names with spaces ('my skill'); auto-generated names with colons or slashes from tool IDs; localized names with non-ASCII characters.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/be66c09b28e69da2. Report an issue: GitHub.