{"record":{"id":"82021c528d22c31f","repo":"hashicorp/packer","slug":"request-github-oidc-token-unexpected-status-s","errorCode":null,"errorMessage":"request GitHub OIDC token: unexpected status %s","messagePattern":"request GitHub OIDC token: unexpected status (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/attestation/sign_keyless.go","lineNumber":350,"sourceCode":"\t\tquery.Set(\"audience\", \"sigstore\")\n\t\tparsedURL.RawQuery = query.Encode()\n\t}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, parsedURL.String(), nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create GitHub OIDC request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+requestToken)\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"request GitHub OIDC token: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode/100 != 2 {\n\t\treturn \"\", fmt.Errorf(\"request GitHub OIDC token: unexpected status %s\", resp.Status)\n\t}\n\n\tvar payload struct {\n\t\tValue string `json:\"value\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode GitHub OIDC token response: %w\", err)\n\t}\n\treturn strings.TrimSpace(payload.Value), nil\n}\n","sourceCodeStart":332,"sourceCodeEnd":361,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/internal/attestation/sign_keyless.go#L332-L361","documentation":"GitHub's OIDC token endpoint responded, but with a non-2xx status code. The full HTTP status string (e.g. \"403 Forbidden\", \"401 Unauthorized\") is embedded in the error. This means the audience-augmented token request was rejected by the server, typically due to authentication or permissions.","triggerScenarios":"resolveGitHubActionerIDToken receives a response whose StatusCode/100 != 2: the ACTIONS_ID_TOKEN_REQUEST_TOKEN is expired/invalid, the workflow lacks id-token: write permission, the repository/organization restricts OIDC, or the custom endpoint (proxy/mock) returns an error status.","commonSituations":"GitHub Actions job without `permissions: id-token: write` (403/404); reusing an OIDC request token from a different job after it expired (401); enterprise policy disabling OIDC for the repo; pointing ACTIONS_ID_TOKEN_REQUEST_URL at a stub server returning 500.","solutions":["Read the embedded status: 403/404 usually means missing `permissions: id-token: write`; 401 means the request token is invalid or expired.","Add `permissions: id-token: write` to the workflow/job in GitHub Actions.","Ensure the token request happens within the same job run that issued ACTIONS_ID_TOKEN_REQUEST_TOKEN.","Confirm the repository/organization OIDC policy allows token issuance.","Bypass the endpoint by supplying SIGSTORE_ID_TOKEN directly."],"exampleFix":"// before (workflow)\njobs:\n  sign:\n    steps:\n      - uses: actions/checkout@v4\n// after\njobs:\n  sign:\n    permissions:\n      id-token: write # also add contents: read if needed\n      contents: read\n    steps:\n      - uses: actions/checkout@v4","handlingStrategy":"type-guard","validationCode":"// inspect the status embedded in the error and branch\nswitch {\ncase strings.Contains(err.Error(), \"403\"), strings.Contains(err.Error(), \"404\"):\n    return fmt.Errorf(\"workflow lacks id-token: write permission\")\ncase strings.Contains(err.Error(), \"401\"):\n    return fmt.Errorf(\"OIDC request token invalid or expired\")\n}","typeGuard":"func isOIDCPermissionError(err error) bool {\n    return err != nil && (strings.Contains(err.Error(), \"403\") || strings.Contains(err.Error(), \"404\")) &&\n        strings.Contains(err.Error(), \"unexpected status\")\n}\nfunc isOIDCAuthError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"401\") && strings.Contains(err.Error(), \"unexpected status\")\n}","tryCatchPattern":"signer, err := newKeylessSigner(ctx, cfg)\nif err != nil && strings.Contains(err.Error(), \"unexpected status\") {\n    if isOIDCPermissionError(err) {\n        return fmt.Errorf(\"add `permissions: id-token: write` to the workflow\")\n    }\n    return fmt.Errorf(\"GitHub OIDC endpoint rejected the token request: %w\", err)\n}","preventionTips":["Always set `permissions: id-token: write` on signing workflows.","Request the OIDC token within the same job that signs — request tokens are short-lived.","Check organization/repository OIDC policies before enabling keyless signing.","Keep SIGSTORE_ID_TOKEN as a fallback path for environments where OIDC is disabled."],"tags":["go","http","github-actions","oidc","permissions"],"backgroundTag":"http-403-forbidden","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}