denoland/deno · error

Failed to get OIDC token: status {}, response: '{}'

Error message

Failed to get OIDC token: status {}, response: '{}'

What it means

When publishing tokenless from GitHub Actions via OIDC, Deno mints a JSR-audience token by calling the Actions OIDC endpoint (`ACTIONS_ID_TOKEN_REQUEST_URL`) with the job's bearer token. Any non-2xx reply produces this error with the HTTP status and the response body. Typical bodies are 'token expired' (OIDC tokens are short-lived) or permission/audience errors from GitHub.

Source

Thrown at cli/tools/publish/mod.rs:782

        );

        let response = client
          .get(url.parse()?)?
          .header(
            http::header::AUTHORIZATION,
            format!("Bearer {}", oidc_config.token).parse()?,
          )
          .send()
          .await
          .context("Failed to get OIDC token")?;
        let status = response.status();
        let text = crate::http_util::body_to_string(response)
          .await
          .with_context(|| {
            format!("Failed to get OIDC token: status {}", status)
          })?;
        if !status.is_success() {
          bail!(
            "Failed to get OIDC token: status {}, response: '{}'",
            status,
            text
          );
        }
        let registry::OidcTokenResponse { value } = serde_json::from_str(&text)
          .with_context(|| {
            format!(
              "Failed to parse OIDC token: '{}' (status {})",
              text, status
            )
          })?;

        let authorization: Rc<str> = format!("githuboidc {}", value).into();
        for pkg in chunked_packages.next().unwrap() {
          authorizations.insert(
            (pkg.scope.clone(), pkg.package.clone(), pkg.version.clone()),
            authorization.clone(),

View on GitHub (pinned to f7822238ca)

Solutions

  1. Re-run the job — an expired/short-lived OIDC token is the most common cause and a retry mints a fresh one.
  2. Add `permissions: id-token: write` (and `contents: read`) to the publishing job or workflow.
  3. If it persists, publish with an explicit token: `deno publish --token <JSR_TOKEN>`.

Example fix

# .github/workflows/publish.yml (before)
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: deno publish
# after
jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: deno publish
Defensive patterns

Strategy: retry

Validate before calling

#!/usr/bin/env bash
# verify the OIDC prerequisites before publishing
[ "${GITHUB_ACTIONS:-}" = "true" ] || { echo "not in GitHub Actions — use --token or skip" >&2; exit 1; }
[ -n "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || { echo "job needs permissions: id-token: write" >&2; exit 1; }

Try / catch

#!/usr/bin/env bash
for attempt in 1 2 3; do
  out="$(deno publish 2>&1)" && exit 0
  if printf '%s' "$out" | grep -q 'Failed to get OIDC token'; then
    echo "OIDC mint failed (attempt $attempt) — backing off" >&2
    sleep $((attempt * 30))   # a retry mints a fresh, unexpired token
    continue
  fi
  printf '%s\n' "$out" >&2; exit 1
done
exit 1

Prevention

When it happens

Trigger: Auth method resolved to OIDC (`GITHUB_ACTIONS=true`, `ACTIONS_ID_TOKEN_REQUEST_URL`/`ACTIONS_ID_TOKEN_REQUEST_TOKEN` present, no `--token`) and the token endpoint answered with status >= 400 — e.g. 403 expired token on long-running multi-chunk publishes, 401 when the job lacks id-token permission.

Common situations: Slow CI publish jobs whose minted token expires; workflows missing `permissions: id-token: write`; transient GitHub OIDC service errors; large workspaces published in 16-package chunks.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/675a063a3c73508e. Report an issue: GitHub.