n8n-io/n8n · error · Error
Invalid package specification
Error message
Invalid package specification
What it means
Thrown by resolvePackage when packageSpec fails the regex /^[a-zA-Z0-9@/_.-]+$/. The check is a command-injection guard: packageSpec is later interpolated into spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`]) and must never carry shell metacharacters or whitespace.
Source
Thrown at packages/@n8n/scan-community-package/scanner/scanner.mjs:58
*
* @throws {UnexpectedError} If the resulting path is not contained within the parentPath.
*/
export function safeJoinPath(parentPath, ...paths) {
const candidate = path.join(parentPath, ...paths);
if (!isContainedWithin(parentPath, candidate)) {
throw new Error(
`Path traversal detected, refusing to join paths: ${parentPath} and ${JSON.stringify(paths)}`,
);
}
return candidate;
}
export const resolvePackage = (packageSpec) => {
// Validate input to prevent command injection
if (!/^[a-zA-Z0-9@/_.-]+$/.test(packageSpec)) {
throw new Error('Invalid package specification');
}
let packageName, version;
if (packageSpec.startsWith('@')) {
if (packageSpec.includes('@', 1)) {
// Handle scoped packages with versions
const lastAtIndex = packageSpec.lastIndexOf('@');
return {
packageName: packageSpec.substring(0, lastAtIndex),
version: packageSpec.substring(lastAtIndex + 1),
};
} else {
// Handle scoped packages without version
return { packageName: packageSpec, version: null };
}
}
// Handle regular packages
const parts = packageSpec.split('@');View on GitHub (pinned to 5ac6606e81)
Solutions
- Pass only a plain registry package spec: 'name', '@scope/name', 'name@version', or '@scope/name@version'.
- If you need a git/url source, fetch and resolve it outside resolvePackage and feed the resulting name@version in.
- Strip leading/trailing whitespace from user input before calling resolvePackage.
- If a legitimate character is rejected, extend the allowlist regex deliberately rather than widening it broadly.
Example fix
// before
resolvePackage(userInput); // userInput = 'my pkg#' -> throws
// after - normalize then validate
const spec = String(userInput ?? '').trim();
if (!/^[a-zA-Z0-9@/_.-]+$/.test(spec)) {
throw new Error(`Unsupported package spec; use name@version form: ${JSON.stringify(spec)}`);
}
const { packageName, version } = resolvePackage(spec); Defensive patterns
Strategy: validation
Validate before calling
const PACKAGE_SPEC_RE = /^[a-zA-Z0-9@/_.-]+$/;
function isValidPackageSpec(spec: unknown): spec is string {
return typeof spec === 'string' && PACKAGE_SPEC_RE.test(spec) && spec.trim().length > 0;
}
if (!isValidPackageSpec(userInput)) {
throw new Error('Package spec must be name, @scope/name, name@version, or @scope/name@version');
} Type guard
function isRegistrySpec(spec: string): boolean {
// Reject git/url specs up front - they legitimately fail the regex but for a different reason.
return !spec.startsWith('git+') && !spec.startsWith('file:') && !spec.startsWith('http') && /^[a-zA-Z0-9@/_.-]+$/.test(spec);
} Prevention
- Always validate package specs with the allowlist regex before calling resolvePackage.
- Reject git/url/local specs explicitly with a clearer message than the generic regex rejection.
- Trim whitespace from user input before validation.
- Never broaden the regex to accept spaces or shell metacharacters - it exists to prevent command injection.
When it happens
Trigger: Calling resolvePackage with a spec containing spaces, quotes, semicolons, ampersands, pipe characters, backticks, '$', parentheses, or any non-ASCII character. Also rejects specs with backslashes or colons.
Common situations: Passing a git URL or tarball URL instead of a registry name (e.g. 'git+https://...'); a local file path spec ('./my-package'); a spec copied from a shell command that includes flags ('pkg --registry=...'); a typo introducing a disallowed character.
Related errors
- Invalid skill at ${sourceDirectory}: ${errors.join('; ')}
- Webhook URL must use HTTPS. Got: ${url.protocol}
- Webhook URL cannot target localhost
- Webhook URL cannot target private/internal IP addresses
- Webhook URL cannot target internal hostname: ${hostname}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/349e3b12b2b93ade.
Report an issue: GitHub.