RocketChat/Rocket.Chat · warning · Error
Invalid Api parameter provided, it must be a valid IApi…
Error message
Invalid Api parameter provided, it must be a valid IApi object.
What it means
Rocket.Chat's POST /oauth/authorize handler (the OAuth2 consent step) responds 401 with the standard RFC 6749 error 'access_denied' when the submitted form does not contain allow='yes'. It means the resource owner (the logged-in Rocket.Chat user) declined to authorize the client application, or the request simply lacks the allow field. This is the consent screen's 'Cancel/Deny' outcome, also hit by clients that post to the endpoint programmatically without replicating the form.
Solutions
- If the user intentionally denied, treat access_denied as terminal: show a message or redirect back with error=access_denied instead of retrying
- When the user approves, make sure the consent form POSTs allow='yes' together with access_token (or legacy token), client_id, redirect_uri and the other OAuth params
- For programmatic calls, include allow=yes in the form-urlencoded body
- Verify no middleware/proxy strips form fields from the POST body
Example fix
// before
await fetch('/oauth/authorize', { method: 'POST', body: new URLSearchParams({ client_id, redirect_uri, response_type: 'code' }) });
// after
await fetch('/oauth/authorize', { method: 'POST', body: new URLSearchParams({ client_id, redirect_uri, response_type: 'code', allow: 'yes', access_token }) }); Defensive patterns
Strategy: validation
Validate before calling
const params = new URLSearchParams(formBody);
if (params.get('allow') !== 'yes') { /* show the consent UI; do not POST /oauth/authorize yet */ } Try / catch
After the POST, branch on the parsed body: if (res.status === 401 && body.error === 'access_denied') treat it as a user decision — stop the flow and inform/re-prompt; do not auto-retry the same denied request.
Prevention
- Always submit allow=yes together with the OAuth params when the user approves
- Handle the access_denied error branch in the OAuth client redirect handler
- Never cache or replay consent submissions across user sessions
When it happens
Trigger: POST /oauth/authorize with req.body.allow !== 'yes': the user clicked Deny/Cancel on the authorization dialog; the consent form was submitted without the hidden allow='yes' field; a curl or script replay of the flow omitted allow.
Common situations: Third-party apps integrating Rocket.Chat OAuth2; headless clients that skip or automate the consent screen; customized/AJAX-submitted consent forms that drop the allow field; manual curl testing of the authorization step.
Related errors
- Invalid command parameter provided, must be a string.
- 403
- authorization failed when sending push to gateway. not…
- cannot-access-room
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/e4d5e51741137146.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/app/apps/server/bridges/api.ts:92
router[method](
routePath,
authenticationMiddleware({ rejectUnauthorized: !!endpoint.authRequired }),
Meteor.bindEnvironment(this._appApiExecutor(endpoint, appId)),
);
}
}
public async unregisterApis(appId: string): Promise<void> {
this.orch.debugLog(`The App ${appId} is unregistering all apis`);
if (this.appRouters.get(appId)) {
this.appRouters.delete(appId);
}
}
private _verifyApi(api: IApi, endpoint: IApiEndpoint): void {
if (typeof api !== 'object') {
throw new Error('Invalid Api parameter provided, it must be a valid IApi object.');
}
if (typeof endpoint.path !== 'string') {
throw new Error('Invalid Api parameter provided, it must be a valid IApi object.');
}
}
private _appApiExecutor(endpoint: IApiEndpoint, appId: string): RequestHandler {
return (req: IRequestWithPrivateHash, res: Response): void => {
const request: IApiRequest = {
method: req.method.toLowerCase() as RequestMethod,
headers: req.headers as { [key: string]: string },
query: (req.query as { [key: string]: string }) || {},
params: req.params || {},
content: req.body,
privateHash: req._privateHash,
user: req.user && this.orch.getConverters()?.get('users')?.convertToApp(req.user),
};View on GitHub (pinned to b2c16d5842)