router-for-me/CLIProxyAPI · error

redirect location is not absolute

Error message

redirect location is not absolute

What it means

Once Location is resolved against the base URL, pluginStoreRedirectURL requires the result to be absolute (non-empty Scheme and Host). 'redirect location is not absolute' fires when the resolved next URL still lacks scheme/host — possible when the base itself was relative (so resolution could not produce an absolute result) or the Location is scheme-relative like '//host/path' resolved against a scheme-less base.

Source

Thrown at internal/pluginstore/github.go:257

		return false
	}
}

func pluginStoreRedirectURL(resp *http.Response, requestURL string) (string, error) {
	location := strings.TrimSpace(resp.Header.Get("Location"))
	if location == "" {
		return "", fmt.Errorf("redirect missing Location header")
	}
	base, errBase := url.Parse(requestURL)
	if errBase != nil {
		return "", fmt.Errorf("parse redirect base: %w", errBase)
	}
	next, errNext := base.Parse(location)
	if errNext != nil {
		return "", fmt.Errorf("parse redirect location: %w", errNext)
	}
	if next.Scheme == "" || next.Host == "" {
		return "", fmt.Errorf("redirect location is not absolute")
	}
	return next.String(), nil
}

func readPluginStoreResponse(resp *http.Response, maxSize int64, authenticated bool) ([]byte, error) {
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.WithError(errClose).Debug("failed to close plugin store response body")
		}
	}()
	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
		if authenticated {
			return nil, fmt.Errorf("unexpected status %d", resp.StatusCode)
		}
		body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
		return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
	}
	reader := io.Reader(resp.Body)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Ensure the URL passed into the plugin store client is absolute (scheme + host), e.g. https://api.github.com/... or a full https artifact URL.
  2. Fix manifest URLs that are stored as paths without a host.
  3. If a server emits scheme-relative Locations, that is fine as long as your base URL is absolute — verify the base first.

Example fix

# before (manifest)
url: /downloads/p-linux-amd64   # relative -> 'redirect location is not absolute' on redirect

# after (manifest)
url: https://example.com/downloads/p-linux-amd64
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(baseURL)
if err != nil || u.Scheme == "" || u.Host == "" {
    return fmt.Errorf("plugin url must be absolute, got %q", baseURL)
}

Prevention

When it happens

Trigger: A Location such as '//cdn.example.com/file' or '/file' resolved against a request URL that itself has no scheme/host (e.g. the client was invoked with a relative or path-only URL, or a custom HTTPDoer context). Scheme-relative Locations against a proper https base resolve fine, so the usual culprit is a relative base URL.

Common situations: Calling the plugin store client with a relative base URL in tests or behind a URL-rewriting proxy; misconfigured manifests with path-only URLs.

Related errors


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