golang/go · error · module.VersionError

invalid response from proxy %q: %w

Error message

invalid response from proxy %q: %w

What it means

proxyRepo.Stat unmarshals the JSON body of `@v/{encRev}.info` into a RevInfo. If the proxy returns non-JSON or malformed JSON, json.Unmarshal fails and the error is wrapped with the proxy's redacted base URL for diagnosis.

Source

Thrown at src/cmd/go/internal/modfetch/proxy.go:380

		return nil, p.versionError("", codehost.ErrNoCommits)
	}

	// Call Stat to get all the other fields, including Origin information.
	return p.Stat(ctx, bestVersion)
}

func (p *proxyRepo) Stat(ctx context.Context, rev string) (*RevInfo, error) {
	encRev, err := module.EscapeVersion(rev)
	if err != nil {
		return nil, p.versionError(rev, err)
	}
	data, err := p.getBytes(ctx, "@v/"+encRev+".info")
	if err != nil {
		return nil, p.versionError(rev, err)
	}
	info := new(RevInfo)
	if err := json.Unmarshal(data, info); err != nil {
		return nil, p.versionError(rev, fmt.Errorf("invalid response from proxy %q: %w", p.redactedBase, err))
	}
	if info.Version != rev && rev == module.CanonicalVersion(rev) && module.Check(p.path, rev) == nil {
		// If we request a correct, appropriate version for the module path, the
		// proxy must return either exactly that version or an error — not some
		// arbitrary other version.
		return nil, p.versionError(rev, fmt.Errorf("proxy returned info for version %s instead of requested version", info.Version))
	}
	return info, nil
}

func (p *proxyRepo) Latest(ctx context.Context) (*RevInfo, error) {
	data, err := p.getBytes(ctx, "@latest")
	if err != nil {
		if !errors.Is(err, fs.ErrNotExist) {
			return nil, p.versionError("", err)
		}
		return p.latest(ctx)
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Inspect what the proxy actually returned: curl -i {proxy}/{module}/@v/{ver}.info.
  2. Bypass the failing proxy: GOPROXY=direct or GONOPROXY for the path.
  3. If you operate the proxy, ensure .info returns strict JSON: {"Version":"...","Time":"...","Origin":{...}}.
  4. Add the proxy host to NO_PROXY if it is intercepting all traffic unintentionally.
Defensive patterns

Strategy: try-catch

Validate before calling

// Smoke-test the .info endpoint before relying on Stat in automation.
func probeInfo(proxy, mod, ver string) error {
    resp, err := http.Get(proxy + "/" + mod + "/@v/" + ver + ".info")
    if err != nil { return err }
    defer resp.Body.Close()
    var v struct{ Version, Time string }
    return json.NewDecoder(resp.Body).Decode(&v)
}

Try / catch

_, err := repo.Stat(ctx, rev)
if err != nil {
    if strings.Contains(err.Error(), "invalid response from proxy") {
        // fall back to direct or another proxy entry
    }
}

Prevention

When it happens

Trigger: getBytes succeeds (200 OK) but body is HTML error page, partial JSON, wrong content type, or otherwise invalid; json.Unmarshal returns non-nil.

Common situations: Corporate proxy returning an HTML login/captive portal; proxy returning a 200 with an error envelope; misbehaving self-hosted proxy; transient CDN corruption.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/56c8fde511308a87. Report an issue: GitHub.