{"record":{"id":"81c5c507ae47c17b","repo":"router-for-me/CLIProxyAPI","slug":"invalid-release-tag-q","errorCode":null,"errorMessage":"invalid release tag %q","messagePattern":"invalid release tag %q","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/pluginstore/github.go","lineNumber":115,"sourceCode":"\t\turl.PathEscape(tag),\n\t)\n\tdata, errDownload := c.get(ctx, releaseURL, \"application/vnd.github+json\", RequestKindMetadata, 0)\n\tif errDownload != nil {\n\t\treturn Release{}, errDownload\n\t}\n\tvar release Release\n\tif errDecode := json.Unmarshal(data, &release); errDecode != nil {\n\t\treturn Release{}, fmt.Errorf(\"decode release: %w\", errDecode)\n\t}\n\treturn release, nil\n}\n\n// ReleaseVersion derives the plugin version from the release tag, stripping a\n// leading \"v\"/\"V\" and validating the result.\nfunc ReleaseVersion(release Release) (string, error) {\n\tversion := normalizeVersion(release.TagName)\n\tif !validPluginVersion(version) {\n\t\treturn \"\", fmt.Errorf(\"invalid release tag %q\", release.TagName)\n\t}\n\treturn version, nil\n}\n\nfunc (c Client) DownloadAsset(ctx context.Context, asset ReleaseAsset) ([]byte, error) {\n\tdownloadURL := strings.TrimSpace(asset.BrowserDownloadURL)\n\tapiURL := strings.TrimSpace(asset.APIURL)\n\tif downloadURL == \"\" || c.releaseAssetAPIAuthenticated(apiURL) {\n\t\tif apiURL != \"\" {\n\t\t\tdownloadURL = apiURL\n\t\t}\n\t}\n\tif downloadURL == \"\" {\n\t\treturn nil, fmt.Errorf(\"asset %q missing download url\", asset.Name)\n\t}\n\treturn c.get(ctx, downloadURL, \"application/octet-stream\", RequestKindArtifact, 0)\n}\n","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/github.go#L97-L133","documentation":"ReleaseVersion converts a GitHub release TagName into a plugin version: normalizeVersion strips a leading v/V, then validPluginVersion checks the result. 'invalid release tag %q' means the tag — after stripping the prefix — is not an acceptable plugin version string. The raw tag is included in the error for diagnostics.","triggerScenarios":"A release tagged with something that is not a valid version after v/V stripping — e.g. 'v', 'latest', 'nightly-2024', 'release-1' or tags with whitespace/special characters, depending on validPluginVersion's rules (typically semver-like).","commonSituations":"Plugin maintainers tagging releases as 'stable', 'prod', or date-only tags; automation creating ad-hoc tags; tags like 'v2' (incomplete semver) if the validator requires full MAJOR.MINOR.PATCH.","solutions":["Retag the release using a version the validator accepts, e.g. 'v1.2.3' instead of 'v1' or 'stable'.","Check validPluginVersion in internal/pluginstore to see the exact accepted grammar before choosing a tag scheme.","If you cannot control the tag, map it to a synthetic version before invoking release-based install."],"exampleFix":"# before\ngit tag stable && git push --tags\n# install fails: 'invalid release tag \"stable\"'\n\n# after\ngit tag v1.2.3 && git push --tags\n# install derives version 1.2.3","handlingStrategy":"validation","validationCode":"v := strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(release.TagName), \"v\"), \"V\")\nif !looksLikeVersion(v) { // e.g. regexp ^\\d+\\.\\d+\\.\\d+\n    return fmt.Errorf(\"release tag %q is not a version — retag as vX.Y.Z\", release.TagName)\n}","typeGuard":"func releaseTagIsVersion(tag string) bool {\n    v := strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(tag), \"v\"), \"V\")\n    matched, _ := regexp.MatchString(`^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?$`, v)\n    return matched\n}","tryCatchPattern":null,"preventionTips":["Adopt strict semver tags (vMAJOR.MINOR.PATCH) in release automation.","Reject non-version tags at CI time, before users can install them."],"tags":["release-tag","version-validation","github-api","plugin-store"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}