PKCE code_verifier mismatch with JS SDK OAuth flow

Running into a wall with the Authorization Code flow using PKCE for a custom SPA. The initial redirect works fine, but the token exchange hits a 400 Bad Request. The error payload says code_verifier does not match code_challenge.

I’m generating the challenge using the standard base64url encoding of the SHA-256 hash. Here’s the setup:

const codeVerifier = crypto.randomUUID();
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const hash = await crypto.subtle.digest('SHA-256', data);
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(hash)))
 .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');

Passing code_challenge_method=S256 in the auth request. When the callback fires, I send code and code_verifier to /api/v2/oauth/token. The verifier is the exact same string. Debug logs show they match character for character. Is the SDK doing some hidden transformation on the verifier before sending it? Or is there a specific header requirement I’m missing for this endpoint?