router-for-me/CLIProxyAPI · error
decode release: %w
Error message
decode release: %w
What it means
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.
Source
Thrown at internal/pluginstore/github.go:78
// FetchLatestRelease returns the latest published release of the plugin's
// GitHub repository, mirroring the WebUI panel update check.
func (c Client) FetchLatestRelease(ctx context.Context, plugin Plugin) (Release, error) {
owner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)
if errRepository != nil {
return Release{}, errRepository
}
releaseURL := fmt.Sprintf(
"https://api.github.com/repos/%s/%s/releases/latest",
url.PathEscape(owner),
url.PathEscape(repo),
)
data, errDownload := c.get(ctx, releaseURL, "application/vnd.github+json", RequestKindMetadata, 0)
if errDownload != nil {
return Release{}, errDownload
}
var release Release
if errDecode := json.Unmarshal(data, &release); errDecode != nil {
return Release{}, fmt.Errorf("decode release: %w", errDecode)
}
return release, nil
}
// FetchReleaseByTag returns a published release by its exact GitHub tag.
func (c Client) FetchReleaseByTag(ctx context.Context, plugin Plugin, tag string) (Release, error) {
owner, repo, errRepository := GitHubRepositoryParts(plugin.Repository)
if errRepository != nil {
return Release{}, errRepository
}
tag = strings.TrimSpace(tag)
if tag == "" {
return Release{}, fmt.Errorf("release tag is required")
}
releaseURL := fmt.Sprintf(
"https://api.github.com/repos/%s/%s/releases/tags/%s",
url.PathEscape(owner),
url.PathEscape(repo),View on GitHub (pinned to 78f0c4079e)
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.
Example fix
// before
release, err := client.FetchLatestRelease(ctx, plugin)
if err != nil { return err } // 'decode release: invalid character ...'
// after
release, err := client.FetchLatestRelease(ctx, plugin)
if err != nil {
var syntaxErr *json.SyntaxError
if errors.As(err, &syntaxErr) {
log.WithError(err).Error("non-JSON body from GitHub — check proxy/network interception")
}
return err
} Defensive patterns
Strategy: retry
Try / catch
release, err := client.FetchLatestRelease(ctx, plugin)
if err != nil {
var jsonErr *json.SyntaxError
if errors.As(err, &jsonErr) {
log.WithError(err).Warn("non-JSON GitHub response — likely proxy interference; retrying once")
release, err = client.FetchLatestRelease(ctx, plugin)
}
if err != nil { return err }
} Prevention
- Allowlist api.github.com on intercepting proxies.
- Wrap with %w-aware inspection (errors.As on json errors) to distinguish transport from schema problems.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unexpected status %d: %s
- invalid release tag %q
- asset %q missing download url
- stopped after %d redirects
- redirect missing Location header
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/952387f3cb3fd709.
Report an issue: GitHub.