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
- Check the preceding logger warning — it contains the response body with the OAuth error (e.g. invalid_grant, invalid_client)
- Verify client id/secret and callback/redirect URI configuration match the Cloud console
- Retry the full flow from the beginning (fresh authorize redirect) since authorization codes are single-use
- 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
- Log and inspect the warn-level response body — it contains the OAuth error detail
- Keep client id/secret and redirect URI in sync with the Cloud console
- Always restart from the authorize step on callback failure since codes are single-use
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
- Medusa Cloud does not support registration. Use method `auth
- Email not verified, cannot proceed with authentication
- Customer with this email already has an account
- Failed to establish a connection to PostgreSQL. Please ensur
- The user is already authenticated and cannot accept an invit
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/461e3a447091e574.
Report an issue: GitHub.