Hmbown/CodeWhale · error

skill is not an import candidate

Error message

skill is not an import candidate

What it means

Thrown when `import_external` would silently import a skill the caller never opted into: the target owned scope does not already contain the canonical name, and the audit did not mark the skill as `import_candidate`. Import is either explicit-candidate-only or a deduplicating AlreadyPresent no-op.

Solutions

  1. Import the skill through the flow that marks it as an import candidate (accept it from the audit/suggestion listing) instead of calling import directly.
  2. Re-run the audit so `import_candidate` is refreshed, then retry.
  3. Use the correct `SkillTargetScope` where the skill's canonical name already exists if the intent was to refresh an existing import.
Defensive patterns

Strategy: validation

Validate before calling

let owned_has_name = owned_snap.skills.iter().any(|s| s.id.canonical_name == source_id.canonical_name);
if !owned_has_name && !source_skill.import_candidate {
    return Err(anyhow!("skill is not an import candidate"));
}

Prevention

When it happens

Trigger: Calling `import_external` where the owned snapshot has no skill matching `source_id.canonical_name` and `source_skill.import_candidate` is false — i.e. the external skill was never flagged as an import suggestion by the audit.

Common situations: Pointing import at an arbitrary external skill that was never surfaced as a candidate; a stale audit predating the candidate flag; passing a `SkillTargetScope` different from the one that holds the name (so `owned_has_name` is false).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4e6c1d9bb5fb35ef. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/skills/mutation.rs:670

    }
    if source_skill.path_unsafe {
        bail!("refusing to import unsafe external skill package");
    }

    let owned_snap = scan_with_configured(
        ctx.workspace,
        ctx.home,
        ctx.configured_skills_dir,
        SkillAuditMode::OwnedOnly,
        None,
    );
    let owned_has_name = owned_snap
        .skills
        .iter()
        .any(|s| s.id.canonical_name == source_id.canonical_name);

    if !owned_has_name && !source_skill.import_candidate {
        bail!("skill is not an import candidate");
    }

    let before = verify_expected_digest(&source_path, Some(&expected_digest))?;

    let skills_dir = prepare_owned_target(ctx.workspace, ctx.home, target)?;
    let anchor = owned_anchor(ctx.workspace, ctx.home, target)?;

    let want_kind = match target {
        SkillTargetScope::Project => SkillRootKind::CodeWhaleProject,
        SkillTargetScope::Global => SkillRootKind::CodeWhaleGlobal,
    };
    let existing_in_target: Vec<&AuditedSkill> = owned_snap
        .skills
        .iter()
        .filter(|s| s.id.canonical_name == source_id.canonical_name)
        .filter(|s| s.root.kind == want_kind)
        .collect();

View on GitHub (pinned to 73e0f67d83)