router-for-me/CLIProxyAPI · error
release-tag %q resolves version %q, want %q
Error message
release-tag %q resolves version %q, want %q
What it means
For github-release installs, Validate() derives a version from the tag via ReleaseVersion() and compares it to the manifest's normalized version field. If they differ, the manifest is internally inconsistent: the pinned tag would install a different version than the manifest declares, so validation fails.
Source
Thrown at internal/pluginstore/manifest.go:142
return validatePinnedArtifactURLs(plan.Artifacts)
}
return validateManifestSourceURL(m.SourceURL)
case InstallTypeGitHubRelease:
releaseTag := strings.TrimSpace(m.ReleaseTag)
if releaseTag == "" {
return fmt.Errorf("missing required field release-tag")
}
plugin := m.Plugin()
plugin.Install = InstallPlan{Type: InstallTypeGitHubRelease}
if errValidate := ValidatePlugin(plugin); errValidate != nil {
return errValidate
}
releaseVersion, errVersion := ReleaseVersion(Release{TagName: releaseTag})
if errVersion != nil {
return errVersion
}
if releaseVersion != normalizeVersion(version) {
return fmt.Errorf("release-tag %q resolves version %q, want %q", releaseTag, releaseVersion, normalizeVersion(version))
}
return nil
default:
return fmt.Errorf("unsupported install type %q", m.Install.Type)
}
}
func validatePinnedArtifactURLs(artifacts []Artifact) error {
for index, artifact := range artifacts {
parsed, errParse := url.Parse(strings.TrimSpace(artifact.URL))
if errParse != nil {
return fmt.Errorf("artifacts[%d]: invalid artifact url", index)
}
if parsed.User != nil {
return fmt.Errorf("artifacts[%d]: pinned artifact url must not contain credentials", index)
}
if parsed.RawQuery != "" || parsed.Fragment != "" {
return fmt.Errorf("artifacts[%d]: pinned artifact url must not contain query or fragment", index)View on GitHub (pinned to 78f0c4079e)
Solutions
- Make release-tag and version describe the same release, e.g. version: 1.3.0 with release-tag: v1.3.0
- If your tags carry no v-prefix, use release-tag: 1.3.0 — what matters is what ReleaseVersion extracts equals normalizeVersion(version)
- Wire CI to render both fields from the same source (git describe / release variable) so they cannot drift
Example fix
# before version: 1.2.0 release-tag: v1.3.0 # release-tag "v1.3.0" resolves version "1.3.0", want "1.2.0" # after version: 1.3.0 release-tag: v1.3.0
Defensive patterns
Strategy: validation
Validate before calling
rv, err := pluginstore.ReleaseVersion(pluginstore.Release{TagName: m.ReleaseTag})
if err != nil { return err }
norm := m.Version
if len(norm) > 1 && (norm[0] == 'v' || norm[0] == 'V') { norm = norm[1:] }
if rv != norm { return fmt.Errorf("tag %s does not match version %s", m.ReleaseTag, m.Version) } Type guard
func tagMatchesVersion(tag, version string) bool { rv, err := pluginstore.ReleaseVersion(pluginstore.Release{TagName: tag}); return err == nil && rv == strings.TrimPrefix(strings.TrimPrefix(version, "v"), "V") } Try / catch
if err := m.Validate(); err != nil && strings.Contains(err.Error(), "resolves version") { /* re-derive version from the tag and rewrite manifest */ } Prevention
- Single-source both fields from the release tag in CI
- Fail the release job when tag and version diverge
When it happens
Trigger: Manifest with version: 1.2.0 but release-tag: v1.3.0; or tag "release-2024.01" whose extracted version does not equal the version field; or version "1.2" vs tag "v1.2.0" when ReleaseVersion yields "1.2.0".
Common situations: Bumping version in one place but not the other during release automation; renaming tags (v-prefix added/removed) without updating the manifest; cherry-picking a manifest template and only editing one field.
Related errors
- github-release manifest requires a resolved release
- missing required field release-tag
- plugin_manifest_failed
- unsupported install type %q
- missing required field version
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/515edc044804f6bb.
Report an issue: GitHub.