RocketChat/Rocket.Chat · error · Error
Integration payload must be a JSON object, not an array or p
Error message
Integration payload must be a JSON object, not an array or primitive
What it means
Thrown by getBodyParams() in the incoming-webhook body parser when the request is form-urlencoded with a single 'payload' field, the payload parses as valid JSON, but the parsed value is not a plain object (it is an array or a primitive like a string/number/boolean). Slack/GitHub-style webhooks wrap a JSON object in payload; this guard rejects malformed integrations that wrap non-object JSON.
Source
Thrown at apps/meteor/server/api/webhooks.ts:141
* with Content-Type: application/x-www-form-urlencoded (e.g. `payload={"text":"hello"}`).
* This function unwraps it so integrations receive the parsed JSON directly.
*/
function getBodyParams(bodyParams: unknown, request: Request): Record<string, unknown> {
if (!isPlainObject(bodyParams)) {
return {};
}
if (
request.headers.get('content-type')?.startsWith('application/x-www-form-urlencoded') &&
Object.keys(bodyParams).length === 1 &&
typeof bodyParams.payload === 'string'
) {
try {
const parsed = JSON.parse(bodyParams.payload);
// Valid JSON must be an object, not an array or primitive
if (!isPlainObject(parsed)) {
throw new Error('Integration payload must be a JSON object, not an array or primitive');
}
return parsed;
} catch (err) {
// Invalid JSON -> return original bodyParams (backward compatibility)
if (err instanceof SyntaxError) {
return bodyParams;
}
throw err;
}
}
return bodyParams;
}
async function executeIntegrationRest(
this: IntegrationThis,
): Promise<View on GitHub (pinned to f9d3ec372b)
Solutions
- Ensure the upstream sends payload as a JSON object literal, e.g. payload={"text":"hello"}.
- If the upstream legitimately sends an array, wrap it in an object before forwarding: {"events":[...]}
- Switch the Content-Type to application/json and POST the object directly if the wrapper is not required.
Example fix
// before - upstream sends
// Content-Type: application/x-www-form-urlencoded
// payload=[{"text":"hi"}]
// after
// Content-Type: application/x-www-form-urlencoded
// payload={"items":[{"text":"hi"}]} Defensive patterns
Strategy: type-guard
Validate before calling
function isPlainObject(v) { return typeof v === 'object' && v !== null && !Array.isArray(v); }
if (typeof bodyParams.payload === 'string') {
const parsed = JSON.parse(bodyParams.payload);
if (!isPlainObject(parsed)) throw new ClientError('payload must be object');
} Type guard
function isPlainObject(v) {
if (v === null || typeof v !== 'object') return false;
const proto = Object.getPrototypeOf(v);
return proto === Object.prototype || proto === null;
} Prevention
- Configure the upstream to send payload=<object> or use application/json directly.
- Wrap array data in {"items":[...]} before forwarding.
- Test the webhook payload shape against isPlainObject before going live.
When it happens
Trigger: POST to a webhook URL with Content-Type application/x-www-form-urlencoded and body payload=[1,2,3] or payload="hello" or payload=42. The JSON is syntactically valid but not an object, so the parser refuses to substitute it for the body params.
Common situations: An integration forwards an array-wrapped event instead of an object. A misconfigured upstream service sends payload=<string>. A migration tool emits top-level arrays.
Related errors
- username-required
- Invalid integration id or token provided.
- Invalid Api parameter provided, it must be a valid IApi obje
- Failed to parse app.json: ${e instanceof Error ? e.message :
- error-emoji-param-not-provided
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/f6ff6274d060ccb7.
Report an issue: GitHub.