{"record":{"id":"461e3a447091e574","repo":"medusajs/medusa","slug":"could-not-exchange-token-r-status-r-statust","errorCode":null,"errorMessage":"Could not exchange token, ${r.status}, ${r.statusText}","messagePattern":"Could not exchange token, (.+?), (.+?)","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/modules/auth/src/providers/medusa-cloud-auth.ts","lineNumber":167,"sourceCode":"        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\n        },\n        body: new URLSearchParams({\n          client_id: clientId,\n          client_secret: this.config_.api_key,\n          code,\n          redirect_uri: state.callback_url as string,\n          grant_type: \"authorization_code\",\n        }),\n      }).then((r) => {\n        if (!r.ok) {\n          this.logger_.warn(\n            `Could not exchange token, ${r.status}, ${\n              r.statusText\n            }: response: ${JSON.stringify(r)}`\n          )\n          throw new MedusaError(\n            MedusaError.Types.INVALID_DATA,\n            `Could not exchange token, ${r.status}, ${r.statusText}`\n          )\n        }\n\n        return r.json()\n      })\n\n      const { authIdentity, success, error } = await this.verify_(\n        response.id_token as string,\n        authIdentityService\n      )\n\n      return {\n        success,\n        authIdentity,\n        error,\n      }","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/auth/src/providers/medusa-cloud-auth.ts#L149-L185","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify OAuth config before starting the flow\nif (!process.env.MEDUSA_CLOUD_AUTH_CLIENT_ID || !process.env.MEDUSA_CLOUD_AUTH_CLIENT_SECRET) throw new Error('Cloud auth env vars missing')","typeGuard":null,"tryCatchPattern":"try { await validateCallback(...) } catch (e) { if (/Could not exchange token/.test(e.message)) { /* restart OAuth flow from authorize */ } throw e }","preventionTips":["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"],"tags":["auth","oauth","medusa-cloud","network"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}