hashicorp/packer · error

decode GitHub OIDC token response: %w

Error message

decode GitHub OIDC token response: %w

What it means

resolveGitHubActionsIDToken fetches the ambient OIDC token exposed by GitHub Actions and decodes the JSON response body expecting a {"value": "<jwt>"} shape. This error wraps any failure of json.Decoder on the HTTP response body: malformed JSON, an unexpected schema (e.g. a different key name or an error object returned by the endpoint), or an I/O error while reading the body. It is thrown because a token that cannot be parsed is useless downstream — the caller needs the raw JWT string to sign attestations.

Source

Thrown at internal/attestation/sign_keyless.go:357

	}
	req.Header.Set("Authorization", "Bearer "+requestToken)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("request GitHub OIDC token: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode/100 != 2 {
		return "", fmt.Errorf("request GitHub OIDC token: unexpected status %s", resp.Status)
	}

	var payload struct {
		Value string `json:"value"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
		return "", fmt.Errorf("decode GitHub OIDC token response: %w", err)
	}
	return strings.TrimSpace(payload.Value), nil
}

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Verify the workflow job grants 'permissions: id-token: write' so the OIDC endpoint returns a valid token response instead of an error payload.
  2. Check ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN env vars are set and not pointing at a proxy or wrong host.
  3. Re-run the job to rule out a truncated/flaky response body; inspect resp.Body content (dump it before decoding) to see what was actually returned.
  4. If behind a proxy, bypass it for the OIDC request or fix TLS interception that mangles the body.
  5. Upgrade to the latest version of the tool in case the expected response schema changed upstream.

Example fix

// before
var payload struct {
	Value string `json:"value"`
}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
	return "", fmt.Errorf("decode GitHub OIDC token response: %w", err)
}
// after
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
	return "", fmt.Errorf("github OIDC request failed: %s: %s", resp.Status, body)
}
var payload struct {
	Value string `json:"value"`
}
if err := json.Unmarshal(body, &payload); err != nil {
	return "", fmt.Errorf("decode GitHub OIDC token response %q: %w", body, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL") == "" || os.Getenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN") == "" {
	return errors.New("not running in a GitHub Actions OIDC-enabled job: set permissions: id-token: write")
}

Try / catch

token, err := resolveGitHubActionsIDToken(ctx)
if err != nil {
	var decErr *json.SyntaxError
	if strings.Contains(err.Error(), "decode GitHub OIDC token response") {
		// log response status/body, check id-token: write and proxy settings, retry once
	}
	return fmt.Errorf("obtaining GHA id token: %w", err)
}

Prevention

When it happens

Trigger: Calling resolveGitHubActionsIDToken when the OIDC endpoint (ACTIONS_ID_TOKEN_REQUEST_URL with ACTIONS_ID_TOKEN_REQUEST_TOKEN auth) returns a 200 whose body is not the expected {"value": ...} JSON — e.g. an HTML error page from a proxy, a truncated response, or the response format changed.

Common situations: Running inside GitHub Actions but behind a corporate proxy that rewrites responses; wrong ACTIONS_ID_TOKEN_REQUEST_URL pointing at a non-OIDC endpoint; GitHub API behavior/permission changes (job lacks 'id-token: write' permission and an error body is returned that fails JSON decoding); flaky network truncating the response body mid-decode.

Related errors


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/e6d286b370710777. Report an issue: GitHub.