router-for-me/CLIProxyAPI · error

invalid plugin version %q

Error message

invalid plugin version %q

What it means

InstallVersion (and InstallDirect) rejected the version string after normalizeVersion because validPluginVersion failed. Versions must be non-empty semver-like identifiers; a 'v' prefix is normalized away, but empty or malformed strings fail. The quoted value in the message shows the normalized string that was rejected.

Source

Thrown at internal/pluginstore/install.go:95

			return InstallResult{}, errPlugin
		}
		return c.InstallDirect(ctx, plugin, plugin.Install, options)
	case InstallTypeGitHubRelease:
		return c.InstallVersion(ctx, manifest.Plugin(), manifest.ReleaseTag, manifest.Version, options)
	default:
		return InstallResult{}, fmt.Errorf("unsupported install type %q", manifest.Install.Type)
	}
}

// InstallVersion installs a plugin artifact from a fixed release tag/version.
func (c Client) InstallVersion(ctx context.Context, plugin Plugin, releaseTag string, version string, options InstallOptions) (InstallResult, error) {
	if errValidate := ValidatePlugin(plugin); errValidate != nil {
		return InstallResult{}, errValidate
	}
	options = normalizeInstallOptions(options)
	version = normalizeVersion(version)
	if !validPluginVersion(version) {
		return InstallResult{}, fmt.Errorf("invalid plugin version %q", version)
	}
	releaseTag = strings.TrimSpace(releaseTag)
	if releaseTag == "" {
		releaseTag = version
	}
	release, errRelease := c.FetchReleaseByTag(ctx, plugin, releaseTag)
	if errRelease != nil {
		return InstallResult{}, errRelease
	}
	releaseVersion, errVersion := ReleaseVersion(release)
	if errVersion != nil {
		return InstallResult{}, errVersion
	}
	if releaseVersion != version {
		return InstallResult{}, fmt.Errorf("release tag %q resolved version %q, want %q", releaseTag, releaseVersion, version)
	}
	plugin.Version = version
	return c.installRelease(ctx, plugin, release, version, options)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Pass a concrete version like '1.2.3' (a leading 'v' is fine — it is normalized)
  2. If you only have a tag, resolve it to its version first (ReleaseVersion) before calling InstallVersion
  3. Fix the version field in the plugin manifest

Example fix

// before
result, err := client.InstallVersion(ctx, plugin, tag, "latest", opts)

// after
release, _ := client.FetchReleaseByTag(ctx, plugin, tag)
version, err := pluginstore.ReleaseVersion(release) // e.g. "1.2.3"
result, err := client.InstallVersion(ctx, plugin, tag, version, opts)
Defensive patterns

Strategy: validation

Validate before calling

// Mirror of the guard: non-empty, semver-shaped (leading v tolerated).
version := strings.TrimPrefix(strings.TrimSpace(raw), "v")
if !semverLike(version) { // e.g. regexp `^\d+\.\d+\.\d+(-[\w.]+)?$`
    return fmt.Errorf("invalid plugin version %q", raw)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "invalid plugin version") {
    // resolve tag -> version via ReleaseVersion, then retry with the concrete version
}

Prevention

When it happens

Trigger: Calling InstallVersion with version = "", "latest", "1", or characters outside the allowed set; same check in InstallDirect on plugin.Version.

Common situations: Passing a release tag where a version is expected; manifest with missing version field; typo'd version like '1..0' or trailing whitespace-only.

Related errors


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