{"record":{"id":"5b2f021118e1906c","repo":"amruthpillai/reactive-resume","slug":"bad-request-5b2f02","errorCode":"BAD_REQUEST","errorMessage":"redirect_uris entries must be strings","messagePattern":"redirect_uris entries must be strings","errorType":"error_code","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/auth/src/config.ts","lineNumber":117,"sourceCode":"\n\t\ttelemetry: { enabled: false },\n\t\ttrustedOrigins: TRUSTED_ORIGINS,\n\t\trateLimit: {\n\t\t\t...rateLimitConfig.betterAuth.global,\n\t\t\tenabled: isRateLimitEnabled,\n\t\t},\n\n\t\thooks: {\n\t\t\t// biome-ignore lint/suspicious/useAwait: Better Auth requires middleware callbacks to return a Promise.\n\t\t\tbefore: createAuthMiddleware(async (ctx) => {\n\t\t\t\tif (!ctx.path.includes(\"/oauth2/register\")) return;\n\n\t\t\t\tconst body = ctx.body as { redirect_uris?: unknown } | undefined;\n\t\t\t\tconst redirectUris = Array.isArray(body?.redirect_uris) ? body.redirect_uris : [];\n\n\t\t\t\tfor (const uri of redirectUris) {\n\t\t\t\t\tif (typeof uri !== \"string\") {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", { message: \"redirect_uris entries must be strings\" });\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\t!isAllowedOAuthRedirectUri(uri, TRUSTED_ORIGINS, {\n\t\t\t\t\t\t\tallowUnsafe: env.FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI,\n\t\t\t\t\t\t})\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\tmessage: \"redirect_uri is not allowed for dynamic client registration\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t},\n\n\t\tadvanced: {\n\t\t\tdatabase: { generateId },\n\t\t\tuseSecureCookies: authBaseUrl.startsWith(\"https://\"),\n\t\t\tipAddress: { ipAddressHeaders: TRUSTED_IP_HEADERS },","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/auth/src/config.ts#L99-L135","documentation":"Better Auth before-middleware validates POST /oauth2/register (RFC 7591 dynamic client registration). Each entry of body.redirect_uris must be a string; if any entry is a number, object, array, etc., the middleware throws APIError('BAD_REQUEST') → HTTP 400 with this message.","triggerScenarios":"A dynamic-registration client posts a JSON body where redirect_uris contains a non-string element, e.g. [12345] or [{origin:'...'}] or [null]. The Array.isArray check passes but the per-entry typeof !== 'string' check fails.","commonSituations":"Third-party integration or test harness that builds the registration payload from typed objects instead of plain strings; a serialization bug that wraps URIs; a client confusing client_id (number) with a redirect URI.","solutions":["Ensure every element of redirect_uris is a string before posting: redirect_uris: ['https://app.example.com/callback'].","If generating the payload programmatically, String(...) each entry and reject empties.","Validate with a Zod schema z.array(z.string().url()) on the client side to catch shape errors before the request."],"exampleFix":"// before\nconst body = { redirect_uris: [{ url: 'https://app.example.com/cb' }] };\n// after\nconst body = { redirect_uris: ['https://app.example.com/cb'] };","handlingStrategy":"validation","validationCode":"import { z } from 'zod';\nconst body = z.object({ redirect_uris: z.array(z.string().min(1)) }).parse(raw);","typeGuard":"function areStringUris(v: unknown): v is string[] {\n  return Array.isArray(v) && v.every(x => typeof x === 'string' && x.length > 0);\n}","tryCatchPattern":null,"preventionTips":["Always send redirect_uris as an array of literal strings.","Validate the registration payload with Zod before posting.","String() each entry if generated from typed objects."],"tags":["oauth","auth","better-auth","validation","dynamic-client-registration"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}