can1357/oh-my-pi · error · ToolError
skill:// URL requires a skill name: ${url}
Error message
skill:// URL requires a skill name: ${url} What it means
Although the regex matched, the captured skill-name segment was empty. The function requires at least a skill name after skill:// and throws when it is absent. In practice this guards a defensive branch on the URL grammar.
Source
Thrown at packages/coding-agent/src/tools/bash-skill-urls.ts:61
localOptions?: LocalProtocolOptions;
cwd?: string;
sessionFile?: string;
ensureLocalParentDirs?: boolean;
}
/**
* Resolve a single skill:// URL to its absolute filesystem path.
* Does NOT read file content or verify existence.
*/
export function resolveSkillUrlToPath(url: string, skills: readonly Skill[]): string {
const parsed = /^skill:\/\/([^/?#]+)(\/[^?#]*)?(?:[?#].*)?$/.exec(url);
if (!parsed) {
throw new ToolError(`Invalid skill:// URL: ${url}`);
}
let rawSkillSegment = parsed[1];
if (!rawSkillSegment) {
throw new ToolError(`skill:// URL requires a skill name: ${url}`);
}
// Decode percent-encoded colons (%3A) used for namespaced skill names
try {
rawSkillSegment = decodeURIComponent(rawSkillSegment);
} catch {
// Leave as-is if decoding fails
}
// Resolve skill name by longest-prefix match against registered skills.
// This handles namespaced skills ("plugin:skill") where the URI may also
// carry a colon-delimited suffix (e.g., ":1-5" line range).
const { skill, suffix } = matchSkillName(rawSkillSegment, skills);
if (!skill) {
const available = skills.map(s => s.name);
const availableStr = available.length > 0 ? available.join(", ") : "none";
throw new ToolError(`Unknown skill: ${rawSkillSegment}. Available: ${availableStr}`);
}
View on GitHub (pinned to 9690622007)
Solutions
- Include a valid skill name in the URL: skill://my-skill/path.
- Verify the skill-name variable is non-empty before interpolating it into the URL.
- List available skills first (see the skills registry) and pick an existing name.
Example fix
// before
const url = `skill://${skillName ?? ""}/SKILL.md`;
// after
if (!skillName) throw new Error("skill name required");
const url = `skill://${skillName}/SKILL.md`; Defensive patterns
Strategy: validation
Validate before calling
const m = /^skill:\/\/([^/?#]+)/.exec(url);
if (!m || !m[1]) throw new Error(`skill URL missing name: ${url}`); Type guard
function hasSkillName(url: string): boolean {
const m = /^skill:\/\/([^/?#]+)/.exec(url);
return !!m && m[1].length > 0;
} Try / catch
try {
return resolveSkillUrlToPath(url, skills);
} catch (e) {
if (e instanceof ToolError && e.message.includes("requires a skill name")) {
// reconstruct the URL with an explicit skill name
} else throw e;
} Prevention
- Never interpolate an optional variable directly into the skill name slot.
- Validate non-empty name at URL-construction time.
When it happens
Trigger: A URL like "skill://" where the regex's ([^/?#]+) matched via an alternate path producing an empty first segment — practically seen when callers build the URL by concatenating an empty skill-name variable.
Common situations: Template/codegen producing `skill://${name}/...` where `name` is empty; an upstream parser stripping the name as a namespace prefix.
Related errors
- Invalid skill:// URL: ${url}
- Discord attachment URL is invalid
- ${destination} returned an invalid image URL
- Invalid memory glob URL: ${input}
- skill:// URL requires a skill name: skill://<name>
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/6ce5b3e08cd3a4ab.
Report an issue: GitHub.