medusajs/medusa · error · MedusaError

Could not exchange token, ${r.status}, ${r.statusText}

Error message

Could not exchange token, ${r.status}, ${r.statusText}

What it means

During the Medusa Cloud OAuth callback, the code-for-token exchange with the token endpoint returned a non-2xx HTTP status. The error embeds the HTTP status and status text; the provider also logs the full response body beforehand.

Source

Thrown at packages/modules/auth/src/providers/medusa-cloud-auth.ts:167

        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
          client_id: clientId,
          client_secret: this.config_.api_key,
          code,
          redirect_uri: state.callback_url as string,
          grant_type: "authorization_code",
        }),
      }).then((r) => {
        if (!r.ok) {
          this.logger_.warn(
            `Could not exchange token, ${r.status}, ${
              r.statusText
            }: response: ${JSON.stringify(r)}`
          )
          throw new MedusaError(
            MedusaError.Types.INVALID_DATA,
            `Could not exchange token, ${r.status}, ${r.statusText}`
          )
        }

        return r.json()
      })

      const { authIdentity, success, error } = await this.verify_(
        response.id_token as string,
        authIdentityService
      )

      return {
        success,
        authIdentity,
        error,
      }

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Check the preceding logger warning — it contains the response body with the OAuth error (e.g. invalid_grant, invalid_client)
  2. Verify client id/secret and callback/redirect URI configuration match the Cloud console
  3. Retry the full flow from the beginning (fresh authorize redirect) since authorization codes are single-use
  4. If the IdP returned 5xx, wait and retry
Defensive patterns

Strategy: retry

Validate before calling

// verify OAuth config before starting the flow
if (!process.env.MEDUSA_CLOUD_AUTH_CLIENT_ID || !process.env.MEDUSA_CLOUD_AUTH_CLIENT_SECRET) throw new Error('Cloud auth env vars missing')

Try / catch

try { await validateCallback(...) } catch (e) { if (/Could not exchange token/.test(e.message)) { /* restart OAuth flow from authorize */ } throw e }

Prevention

When it happens

Trigger: validateCallback('medusa-cloud', { code, state }) where the fetch to the token endpoint fails: invalid/expired client credentials, wrong redirect URI, expired authorization code, or the IdP is down/returning 4xx/5xx.

Common situations: Wrong MEDUSA_CLOUD_AUTH_CLIENT_ID/SECRET env vars, clock-skewed or already-used authorization code, mismatched callback URL configured in the Cloud console, or network egress blocked from the server.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/461e3a447091e574. Report an issue: GitHub.