jackwener/OpenCLI · error · ArgumentError
Skill path must be non-empty.
Error message
Skill path must be non-empty.
What it means
normalizeSkillPath trims the raw input, converts backslashes to slashes, and rejects empty strings and NUL bytes before any path resolution. An empty/whitespace-only path (or one containing '\0') means there is nothing valid to normalize, so it throws ArgumentError (usage error, exit code 2).
Source
Thrown at src/skills.ts:104
function parseSkillTarget(target: string, relpath: string): { name: string; pathInSkill: string } {
const normalizedTarget = normalizeSkillPath(target);
if (relpath) {
return { name: normalizedTarget, pathInSkill: relpath };
}
const slash = normalizedTarget.indexOf('/');
if (slash === -1) {
return { name: normalizedTarget, pathInSkill: '' };
}
return {
name: normalizedTarget.slice(0, slash),
pathInSkill: normalizedTarget.slice(slash + 1),
};
}
function normalizeSkillPath(raw: string): string {
const normalized = raw.trim().replace(/\\/g, '/');
if (!normalized || normalized.includes('\0')) {
throw new ArgumentError('Skill path must be non-empty.');
}
if (normalized.startsWith('/') || normalized.split('/').some((part) => part === '..')) {
throw new ArgumentError(`Invalid skill path: ${raw}`, 'Use a path relative to an OpenCLI skill directory.');
}
return path.posix.normalize(normalized);
}
function parseFrontmatter(content: string): SkillFrontmatter {
if (!content.startsWith('---\n')) return {};
const end = content.indexOf('\n---', 4);
if (end < 0) return {};
try {
const parsed = yaml.load(content.slice(4, end));
return parsed && typeof parsed === 'object' ? parsed as SkillFrontmatter : {};
} catch {
return parseLooseFrontmatter(content.slice(4, end));
}
}View on GitHub (pinned to 49907e53dc)
Solutions
- Pass a non-empty relative path, e.g. 'SKILL.md'
- Fix the script/config that produced the empty value — echo the variable before calling to confirm it is populated
- Strip stray whitespace and reject NUL characters in your own input handling before calling
- If you meant the skill's main doc, explicitly pass 'SKILL.md'
Example fix
// before
await readOpenCliSkill('grok', process.env.SKILL_FILE ?? '');
// after
const p = process.env.SKILL_FILE?.trim();
if (!p) throw new Error('SKILL_FILE must be set');
await readOpenCliSkill('grok', p); Defensive patterns
Strategy: validation
Validate before calling
if (typeof p !== 'string' || !p.trim() || p.includes('\0')) throw new Error('skill path must be a non-empty string without NUL'); Type guard
function isValidSkillPath(p: unknown): p is string {
return typeof p === 'string' && p.trim().length > 0 && !p.includes('\0');
} Try / catch
try {
const skill = await readOpenCliSkill(name, p);
} catch (e) {
if (e instanceof ArgumentError && /non-empty/i.test(e.message)) console.error('Skill path was empty — check the variable feeding it.');
else throw e;
} Prevention
- Validate env vars/config values are non-empty before interpolation
- Avoid `?? ''` fallbacks that silently produce empty paths
- Trim and check inputs at the boundary of your script
When it happens
Trigger: Calling the skills read path with '' or a whitespace-only string for the in-skill path, or a path containing a NUL byte (e.g. from untrusted/binary input or a malformed config value).
Common situations: Shell variable that failed to expand ($FILE unset becomes empty); template/interpolation bugs producing empty paths; programmatic callers splitting a string incorrectly; config files with 'path:' left blank.
Related errors
- station must not be empty
- ${label} cannot be empty
- crates ${label} cannot be empty
- Instagram note content cannot be empty.
- --keywords is required
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/fec6930adb318c9a.
Report an issue: GitHub.