{"record":{"id":"952387f3cb3fd709","repo":"router-for-me/CLIProxyAPI","slug":"decode-release-w","errorCode":null,"errorMessage":"decode release: %w","messagePattern":"decode release: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/pluginstore/github.go","lineNumber":78,"sourceCode":"// FetchLatestRelease returns the latest published release of the plugin's\n// GitHub repository, mirroring the WebUI panel update check.\nfunc (c Client) FetchLatestRelease(ctx context.Context, plugin Plugin) (Release, error) {\n\towner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)\n\tif errRepository != nil {\n\t\treturn Release{}, errRepository\n\t}\n\treleaseURL := fmt.Sprintf(\n\t\t\"https://api.github.com/repos/%s/%s/releases/latest\",\n\t\turl.PathEscape(owner),\n\t\turl.PathEscape(repo),\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// FetchReleaseByTag returns a published release by its exact GitHub tag.\nfunc (c Client) FetchReleaseByTag(ctx context.Context, plugin Plugin, tag string) (Release, error) {\n\towner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)\n\tif errRepository != nil {\n\t\treturn Release{}, errRepository\n\t}\n\ttag = strings.TrimSpace(tag)\n\tif tag == \"\" {\n\t\treturn Release{}, fmt.Errorf(\"release tag is required\")\n\t}\n\treleaseURL := fmt.Sprintf(\n\t\t\"https://api.github.com/repos/%s/%s/releases/tags/%s\",\n\t\turl.PathEscape(owner),\n\t\turl.PathEscape(repo),","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/github.go#L60-L96","documentation":"Client.FetchLatestRelease GETs https://api.github.com/repos/{owner}/{repo}/releases/latest (with redirect handling and auth headers via the plugin store's `get`) and json.Unmarshals the body into a Release struct. 'decode release: %w' wraps the json error when the 2xx response body is not valid JSON or does not fit the expected shape. It is a protocol-shape error, distinct from HTTP status errors which are raised earlier by readPluginStoreResponse.","triggerScenarios":"api.github.com returns 200 with a non-JSON body — a captive portal/transparent proxy, a rate-limit page that still returns 200, or an API change in the response schema that breaks unmarshaling (type mismatch on a field). The wrapping preserves the underlying json error via %w.","commonSituations":"Corporate proxies or hotspots intercepting HTTPS and serving HTML; GitHub Enterprise endpoints with a different response shape; a body truncated by an intermediary so JSON is malformed.","solutions":["Inspect the wrapped error (errors.Unwrap) — 'invalid character \\'<\\'' means HTML was served, i.e. a proxy/portal problem, not GitHub.","Reproduce with curl -H 'Accept: application/vnd.github+json' against the same releases/latest URL from the same network.","Bypass or configure the intercepting proxy / captive portal for api.github.com.","If it is a genuine schema mismatch on a field, pin to a GitHub API version header or report the field/shape change to this project."],"exampleFix":"// before\nrelease, err := client.FetchLatestRelease(ctx, plugin)\nif err != nil { return err } // 'decode release: invalid character ...'\n\n// after\nrelease, err := client.FetchLatestRelease(ctx, plugin)\nif err != nil {\n    var syntaxErr *json.SyntaxError\n    if errors.As(err, &syntaxErr) {\n        log.WithError(err).Error(\"non-JSON body from GitHub — check proxy/network interception\")\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"release, err := client.FetchLatestRelease(ctx, plugin)\nif err != nil {\n    var jsonErr *json.SyntaxError\n    if errors.As(err, &jsonErr) {\n        log.WithError(err).Warn(\"non-JSON GitHub response — likely proxy interference; retrying once\")\n        release, err = client.FetchLatestRelease(ctx, plugin)\n    }\n    if err != nil { return err }\n}","preventionTips":["Allowlist api.github.com on intercepting proxies.","Wrap with %w-aware inspection (errors.As on json errors) to distinguish transport from schema problems."],"tags":["github-api","json","decode","network","plugin-store"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}