grpc/grpc-go · error

http status %d, body: %s

Error message

http status %d, body: %s

What it means

Returned by sendRequest in sts/sts.go:320 when the STS HTTP exchange returns a non-2xx status. The full response body is included so the caller can see the token server's error. This is the runtime signal that the token-exchange endpoint rejected the request (auth, scope, subject token, etc.) or was unavailable.

Source

Thrown at credentials/sts/sts.go:320

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	// When the http.Client returns a non-nil error, it is the
	// responsibility of the caller to read the response body till an EOF is
	// encountered and to close it.
	body, err := io.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusOK {
		return body, nil
	}
	logger.Warningf("http status %d, body: %s", resp.StatusCode, string(body))
	return nil, fmt.Errorf("http status %d, body: %s", resp.StatusCode, string(body))
}

func tokenInfoFromResponse(respBody []byte) (*tokenInfo, error) {
	respData := &responseParameters{}
	if err := json.Unmarshal(respBody, respData); err != nil {
		return nil, fmt.Errorf("json.Unmarshal(%v): %v", respBody, err)
	}
	if respData.AccessToken == "" {
		return nil, fmt.Errorf("empty accessToken in response (%v)", string(respBody))
	}
	return &tokenInfo{
		tokenType:  respData.TokenType,
		token:      respData.AccessToken,
		expiryTime: time.Now().Add(time.Duration(respData.ExpiresIn) * time.Second),
	}, nil
}

// requestParameters stores all STS request attributes defined in

View on GitHub (pinned to 03255a9237)

Solutions

  1. Read the response body in the error string to identify the OAuth2 error (invalid_grant, invalid_scope, unauthorized_client).
  2. Verify SubjectTokenPath contents are fresh and match SubjectTokenType; refresh the mounted token.
  3. Confirm Audience/Scope/Resource match what the STS server expects.
  4. For transient 5xx, retry with backoff (the STS creds do not retry automatically).

Example fix

// before
opts := sts.Options{
    SubjectTokenPath: "/var/run/secrets/expired-token",
    Audience: "wrong-audience",
}

// after
opts := sts.Options{
    SubjectTokenPath: "/var/run/secrets/fresh-identity-token",
    SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
    Audience: "https://my-grpc-server",
}
Defensive patterns

Strategy: retry

Validate before calling

// Before relying on STS, ensure subject/actor token files are fresh and readable.
if _, err := os.Stat(opts.SubjectTokenPath); err != nil { return err }
b, err := os.ReadFile(opts.SubjectTokenPath)
if err != nil || len(bytes.TrimSpace(b)) == 0 { return errors.New("subject token missing") }

Try / catch

if st, ok := status.FromError(err); ok && st.Code() == codes.Unavailable {
    if strings.Contains(st.Message(), "http status") {
        // parse body for OAuth2 error (invalid_grant/invalid_scope/unauthorized_client)
        // refresh subject token or fix audience/scope; for 5xx, back off and retry
    }
}

Prevention

When it happens

Trigger: The subject token file is expired or wrong; audience/scope mismatch; the STS endpoint requires different SubjectTokenType; network path returns 4xx/5xx (401, 403, 400, 500, 502); subject/actor token file unreadable upstream.

Common situations: Workload identity token expired; misconfigured audience in xDS bootstrap; service-account token audience not matching the STS server; STS endpoint behind a proxy that returns HTML errors; transient 5xx during an outage.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/ea01982616565e6e. Report an issue: GitHub.