can1357/oh-my-pi · error

Unknown skill: ${skillName} Available: ${availableStr}

Error message

Unknown skill: ${skillName}
Available: ${availableStr}

What it means

After extracting the skill name from the URL host, resolve() searches the active skill registry (context.skills or getActiveSkills()) for a matching name. If no skill with that exact name exists, the error lists all currently available skill names (or 'none' if the registry is empty) so the caller can correct the URL. Names are matched exactly — no fuzzy or case-insensitive fallback.

Source

Thrown at packages/coding-agent/src/internal-urls/skill-protocol.ts:63

 * Handler for skill:// URLs.
 */
export class SkillProtocolHandler implements ProtocolHandler {
	readonly scheme = "skill";
	readonly immutable = true;

	async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
		const skills = context?.skills ?? getActiveSkills();

		const skillName = url.rawHost || url.hostname;
		if (!skillName) {
			throw new Error("skill:// URL requires a skill name: skill://<name>");
		}

		const skill = skills.find(s => s.name === skillName);
		if (!skill) {
			const available = skills.map(s => s.name);
			const availableStr = available.length > 0 ? available.join(", ") : "none";
			throw new Error(`Unknown skill: ${skillName}\nAvailable: ${availableStr}`);
		}

		let targetPath: string;
		const urlPath = url.pathname;
		const hasRelativePath = urlPath && urlPath !== "/" && urlPath !== "";

		if (hasRelativePath) {
			const relativePath = decodeURIComponent(urlPath.slice(1));
			validateRelativePath(relativePath);
			targetPath = path.join(skill.baseDir, relativePath);

			const resolvedPath = path.resolve(targetPath);
			const resolvedBaseDir = path.resolve(skill.baseDir);
			if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
				throw new Error("Path traversal is not allowed");
			}
			// Agent Plugin skills (§4.1): the resource must canonically resolve
			// within the plugin root; a dangling or unresolvable path fails closed.

View on GitHub (pinned to 9690622007)

Solutions

  1. Use one of the names listed in the error's 'Available:' line
  2. Check exact spelling and casing — matching is case-sensitive and exact
  3. Verify the skill's plugin/extension is installed and loaded (check discovery logs)
  4. Call getActiveSkills() (or the completion API) to enumerate valid names before resolving
  5. If the skill was renamed, update the stored URL

Example fix

// before
resolve('skill://Code-Review/README.md')
// after
resolve('skill://code-review/README.md') // exact registered name
Defensive patterns

Strategy: fallback

Validate before calling

import { getActiveSkills } from '../extensibility/skills';
const names = new Set(getActiveSkills().map(s => s.name));
if (!names.has(skillName)) {
  throw new Error(`Skill '${skillName}' not loaded. Available: ${[...names].join(', ') || 'none'}`);
}

Type guard

function skillExists(name: string, skills: { name: string }[]): skills is [{ name: string }, ...{ name: string }[]] {
  return skills.some(s => s.name === name);
}

Try / catch

try {
  return await handler.resolve(url, ctx);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Unknown skill:')) {
    const available = err.message.split('Available: ')[1] ?? '';
    // pick closest match or surface the available list to the user
  }
  throw err;
}

Prevention

When it happens

Trigger: resolve('skill://typo-name/...') where 'typo-name' is not in the active skills list; resolving a skill that was removed, renamed, or not yet loaded; resolving in a context where no skills are registered (empty registry).

Common situations: Typos or wrong casing in the skill name (matching is exact); referencing a skill from a plugin that failed to load or was disabled; stale links to renamed skills; running in an environment (test, SDK embed) where skills were never discovered.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/1aa1930d41d6057e. Report an issue: GitHub.