{"record":{"id":"d5a1ad4dcf7986ba","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-redirecturi","errorCode":"error-invalid-redirectUri","errorMessage":"Invalid redirectUri","messagePattern":"Invalid redirectUri","errorType":"http","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/lib/auth/oauth2-server/addOAuthApp.ts","lineNumber":35,"sourceCode":"\tif (!user?.username) {\n\t\t// TODO: username is required, but not always present\n\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'addOAuthApp' });\n\t}\n\n\tif (!(await hasPermissionAsync(uid, 'manage-oauth-apps'))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'addOAuthApp' });\n\t}\n\n\tif (!applicationParams.name || typeof applicationParams.name.valueOf() !== 'string' || applicationParams.name.trim() === '') {\n\t\tthrow new Meteor.Error('error-invalid-name', 'Invalid name', { method: 'addOAuthApp' });\n\t}\n\n\tif (\n\t\t!applicationParams.redirectUri ||\n\t\ttypeof applicationParams.redirectUri.valueOf() !== 'string' ||\n\t\tapplicationParams.redirectUri.trim() === ''\n\t) {\n\t\tthrow new Meteor.Error('error-invalid-redirectUri', 'Invalid redirectUri', {\n\t\t\tmethod: 'addOAuthApp',\n\t\t});\n\t}\n\n\tif (typeof applicationParams.active !== 'boolean') {\n\t\tthrow new Meteor.Error('error-invalid-arguments', 'Invalid arguments', {\n\t\t\tmethod: 'addOAuthApp',\n\t\t});\n\t}\n\n\tconst application = {\n\t\t...applicationParams,\n\t\tredirectUri: parseUriList(applicationParams.redirectUri),\n\t\tclientId: Random.id(),\n\t\tclientSecret: Random.secret(),\n\t\t_createdAt: new Date(),\n\t\t_updatedAt: new Date(),\n\t\t_createdBy: {","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth/oauth2-server/addOAuthApp.ts#L17-L53","documentation":"First redirectUri gate inside addOAuthApp: the value must exist, be a string, and not be whitespace-only. As with the name check, the REST ajv schema already requires a string, so whitespace-only bodies are the realistic trigger; entirely missing or non-string values only occur on direct/internal calls that bypass the route schema.","triggerScenarios":"POST /api/v1/oauth-apps.create with redirectUri '   ' or '\\n'; direct addOAuthApp call omitting redirectUri; textarea input containing only blank lines.","commonSituations":"Copy-pasting a callback URL that is actually empty/whitespace; form state reset between render and submit; programmatic callers assuming redirectUri is optional.","solutions":["Provide the real callback URL, e.g. https://app.example.com/oauth/callback","Trim and non-empty-check redirectUri on the client before submit","When multiple URIs are needed, send them comma- or newline-separated with at least one real URI"],"exampleFix":"// before\n{ \"name\": \"App\", \"active\": true, \"redirectUri\": \" \" }\n\n// after\n{ \"name\": \"App\", \"active\": true, \"redirectUri\": \"https://app.example.com/oauth/callback\" }","handlingStrategy":"type-guard","validationCode":"// client-side: normalize and check the callback URL\nconst redirectUri = String(form.redirectUri ?? '').trim();\nif (!/^https?:\\/\\/.+/.test(redirectUri)) throw new Error('A valid callback URL is required');\nawait post('/oauth-apps.create', { ...form, redirectUri });","typeGuard":"const isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;\n\nconst hasValidRedirectUri = (p: unknown): boolean =>\n  typeof p === 'object' && p !== null && isNonEmptyString((p as any).redirectUri);","tryCatchPattern":"try {\n  await addOAuthApp(params, uid);\n} catch (error) {\n  if (error instanceof Meteor.Error && error.error === 'error-invalid-redirectUri') {\n    setFieldError('redirectUri', 'Enter at least one callback URL');\n  } else {\n    throw error;\n  }\n}","preventionTips":["Trim redirectUri inputs and require an http(s) URL pattern","Pre-fill the field with the client app's known callback URL when available","Never assume redirectUri is optional"],"tags":["oauth-apps","validation","redirect-uri","rest-api"],"backgroundTag":"api-request-validation-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}