sipeed/picoclaw · error

✗ invalid install target %q: %w

Error message

✗  invalid install target %q: %w

What it means

The install target (the part after the registry name) was rejected by registry.ResolveInstallDirName, which normalizes a slug or owner/repo form into an on-disk directory name under <workspace>/skills. Empty, malformed, or unmappable targets fail here, before any download starts.

Source

Thrown at cmd/picoclaw/internal/skills/helpers.go:66

}

// skillsInstallFromRegistry installs a skill from a named registry (e.g. clawhub).
func skillsInstallFromRegistry(cfg *config.Config, registryName, target string) error {
	err := utils.ValidateSkillIdentifier(registryName)
	if err != nil {
		return fmt.Errorf("✗  invalid registry name: %w", err)
	}

	registryMgr := skills.NewRegistryManagerFromToolsConfig(cfg.Tools.Skills)

	registry := registryMgr.GetRegistry(registryName)
	if registry == nil {
		return fmt.Errorf("✗  registry '%s' not found or not enabled. check your config.json.", registryName)
	}

	dirName, err := registry.ResolveInstallDirName(target)
	if err != nil {
		return fmt.Errorf("✗  invalid install target %q: %w", target, err)
	}

	fmt.Printf("Installing skill '%s' from %s registry...\n", target, registryName)

	workspace := cfg.WorkspacePath()
	targetDir := filepath.Join(workspace, "skills", dirName)

	if _, err = os.Stat(targetDir); err == nil {
		return fmt.Errorf("\u2717 skill '%s' already installed at %s", dirName, targetDir)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	if err = os.MkdirAll(filepath.Join(workspace, "skills"), 0o755); err != nil {
		return fmt.Errorf("\u2717 failed to create skills directory: %w", err)
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Use the exact target form the registry expects (slug or owner/repo) per its docs.
  2. Strip trailing slashes and stray quotes.
  3. Read the wrapped error for the specific rule broken.
  4. Confirm the target exists on the registry before installing.

Example fix

# before
picoclaw skills install clawhub/owner/
# after
picoclaw skills install clawhub/owner/repo
Defensive patterns

Strategy: validation

Validate before calling

target = strings.Trim(target, "/")
if _, err := registry.ResolveInstallDirName(target); err != nil {
    return fmt.Errorf("invalid install target %q: %w", target, err)
}

Type guard

func validInstallTarget(r skills.Registry, t string) bool {
    _, err := r.ResolveInstallDirName(t)
    return err == nil
}

Prevention

When it happens

Trigger: Targets like "owner/" (empty repo), "/repo" (empty owner), strings with unexpected path separators or invalid characters, or anything ResolveInstallDirName cannot map — helpers.go:66.

Common situations: Copy-pasting a URL fragment instead of the slug, a trailing slash left by shell tab-completion, or a registry whose expected target syntax differs from what was typed.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/e0ce79a125a136bb. Report an issue: GitHub.