google-gemini/gemini-cli · info
Skill linking cancelled by user.
Error message
Skill linking cancelled by user.
What it means
Thrown when the consent callback (`requestConsent`) returns false, signalling the user declined the prompt that lists which skills will be linked into the target directory. This is intentional user-driven cancellation, not a system failure — the installer treats 'no consent' as a stop condition and surfaces it as a thrown error so callers know nothing was written.
Source
Thrown at packages/cli/src/utils/skillUtils.ts:250
const seenNames = new Map<string, string>();
for (const skill of skills) {
if (seenNames.has(skill.name)) {
throw new Error(
`Duplicate skill name "${skill.name}" found at multiple locations:\n - ${seenNames.get(skill.name)}\n - ${skill.location}`,
);
}
seenNames.set(skill.name, skill.location);
}
const workspaceDir = process.cwd();
const storage = new Storage(workspaceDir);
const targetDir =
scope === 'workspace'
? storage.getProjectSkillsDir()
: Storage.getUserSkillsDir();
if (!(await requestConsent(skills, targetDir))) {
throw new Error('Skill linking cancelled by user.');
}
const resolvedTarget = path.resolve(targetDir);
await fs.mkdir(resolvedTarget, { recursive: true });
const linkedSkills: Array<{ name: string; location: string }> = [];
for (const skill of skills) {
const skillName = skill.name;
const skillSourceDir = path.dirname(skill.location);
const destPath = path.resolve(resolvedTarget, skillName);
const relative = path.relative(resolvedTarget, destPath);
if (isInvalidSubpath(relative)) {
throw new Error('Invalid skill name: Path traversal detected.');
}
const exists = await fs.lstat(destPath).catch(() => null);View on GitHub (pinned to 5024443c72)
Solutions
- Re-run the command and confirm at the prompt if you actually want to install the skills.
- Pass a `--yes` / `--force` equivalent flag or supply a consent callback that returns `true` for non-interactive runs.
- If the decline was policy-driven, review the skill list, whitelist the trusted source, and retry.
- Catch this specific error and treat it as a soft 'cancelled' status rather than a fatal failure in your orchestration code.
Example fix
// before const consent = (skills, dir) => Promise.resolve(false); await linkSkills(source, scope, onLog, consent); // throws // after const consent = (skills, dir) => Promise.resolve(true); await linkSkills(source, scope, onLog, consent);
Defensive patterns
Strategy: try-catch
Try / catch
try {
await linkSkills(source, scope, onLog, consent);
} catch (e) {
if (e instanceof Error && e.message === 'Skill linking cancelled by user.') {
// treat as soft cancel — exit code 0 or a 'cancelled' UI state
process.exitCode = 0;
return;
}
throw e;
} Prevention
- Distinguish user-cancellation from system errors in your orchestrator (unique message or a custom error class).
- In non-interactive pipelines, pass a consent callback that returns true to avoid spurious cancellations.
- Provide a `--yes` flag at the CLI layer that opts out of the prompt.
When it happens
Trigger: The default consent callback was overridden by a caller that prompts the user (CLI confirmation dialog) and the user selected 'no'. Also triggered if a programmatic caller passes a consent function that returns `false` based on policy, or if the consent prompt is auto-denied in a non-interactive environment.
Common situations: Running the link command in a CI/automation context where the prompt defaults to decline; a user reviewing the skill list and choosing not to trust one of them; a wrapper script that auto-answers prompts negatively unless a `--yes` flag is passed.
Related errors
- Execution aborted
- The source argument must be provided.
- The path argument must be provided.
- The skill name must be provided.
- Operation cancelled.
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/e4600694923e0db9.
Report an issue: GitHub.