router-for-me/CLIProxyAPI · error

direct install plugin

Error message

direct install plugin %q version %q: %w

What it means

Wrapped error from directPluginVersion when the resolved plugin's TOP-LEVEL version exactly matches the requested version (normalizeVersion(plugin.Version) == version), so its Install plan is used directly, but ValidateInstallPlan rejects that plan. The id and requested version are included; the underlying validation failure (e.g. invalid/missing artifact entries) is chained via %w.

Solutions

  1. Unwrap the error and read the ValidateInstallPlan message to see which plan field is invalid
  2. Fix the registry entry for that plugin/version: ensure the install plan has at least one well-formed artifact (URL + checksum) for the target platform
  3. If the registry is remote, re-fetch it to rule out a stale cached copy

Example fix

// before: registry entry
// { "id": "p", "version": "1.0.0", "install": { "type": "direct", "artifacts": [] } }
plugin, err := client.ResolveDirect(ctx, manifest) // "direct install plugin \"p\" version \"1.0.0\": ..."

// after
// { "id": "p", "version": "1.0.0", "install": { "type": "direct", "artifacts": [
//     { "url": "https://cdn.example.com/p-1.0.0-linux-amd64.zip", "checksum": "sha256:...", "os": "linux", "arch": "amd64" } ] } }
Defensive patterns

Strategy: try-catch

Try / catch

plugin, err := client.ResolveDirect(ctx, manifest)
if err != nil {
    if strings.Contains(err.Error(), fmt.Sprintf("direct install plugin %q version %q:", manifest.ID, manifest.Version)) {
        cause := errors.Unwrap(err) // ValidateInstallPlan detail
        return fmt.Errorf("registry install plan invalid for %s@%s: %w", manifest.ID, manifest.Version, cause)
    }
    return err
}

Prevention

When it happens

Trigger: Registry entry whose top-level version matches the requested one but whose install plan is invalid: zero usable artifacts, malformed artifact URLs, missing checksums, or an invalid plan type after NormalizeInstallPlan + forced InstallTypeDirect.

Common situations: Registry published with an incomplete artifacts list for the current version; artifact entries filtered out because they lack URLs; registry hand-edited and plan fields left blank; schema change in the registry format leaving plan fields unparsed.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/fe36f9782605c7e5. Report an issue: GitHub.

Appendix: source

Thrown at internal/pluginstore/install.go:219

	resolved, okPlugin := registry.PluginByID(manifest.ID)
	if !okPlugin {
		return Plugin{}, fmt.Errorf("direct install plugin %q not found in source", strings.TrimSpace(manifest.ID))
	}
	if PluginInstallType(resolved) != InstallTypeDirect {
		return Plugin{}, fmt.Errorf("direct install plugin %q resolved as %q", strings.TrimSpace(manifest.ID), PluginInstallType(resolved))
	}
	return directPluginVersion(resolved, manifest.ID, manifest.Version)
}

func directPluginVersion(plugin Plugin, id string, version string) (Plugin, error) {
	id = strings.TrimSpace(id)
	version = normalizeVersion(version)
	if normalizeVersion(plugin.Version) == version {
		plugin.Version = version
		plugin.Install = NormalizeInstallPlan(plugin.Install)
		plugin.Install.Type = InstallTypeDirect
		if errPlan := ValidateInstallPlan(plugin.Install); errPlan != nil {
			return Plugin{}, fmt.Errorf("direct install plugin %q version %q: %w", id, version, errPlan)
		}
		return plugin, nil
	}
	for _, candidate := range plugin.Versions {
		if normalizeVersion(candidate.Version) != version {
			continue
		}
		plugin.Version = version
		plugin.Install = NormalizeInstallPlan(candidate.Install)
		if plugin.Install.Type == "" {
			plugin.Install.Type = InstallTypeDirect
		}
		if plugin.Install.Type != InstallTypeDirect {
			return Plugin{}, fmt.Errorf("direct install plugin %q version %q resolved as %q", id, version, plugin.Install.Type)
		}
		if errPlan := ValidateInstallPlan(plugin.Install); errPlan != nil {
			return Plugin{}, fmt.Errorf("direct install plugin %q version %q: %w", id, version, errPlan)
		}

View on GitHub (pinned to 78f0c4079e)