RocketChat/Rocket.Chat · warning · Error
Param "${params.customFields.key}" must be an object if prov
Error message
Param "${params.customFields.key}" must be an object if provided What it means
Thrown during channel creation when params.customFields.value is present but not an object. The guard uses typeof === 'object' WITHOUT an Array.isArray exclusion, so an array will pass this check (arrays are objects) - but a string, number, or boolean will fail. The param key name is interpolated.
Source
Thrown at apps/meteor/server/api/v1/channels.ts:1026
const team = teamId && (await Team.getInfoById(teamId));
if (
(!teamId && !(await hasPermissionAsync(params.user.value, 'create-c'))) ||
(teamId && team && !(await hasPermissionAsync(params.user.value, 'create-team-channel', team.roomId)))
) {
throw new Error('unauthorized');
}
if (!params.name?.value) {
throw new Error(`Param "${params.name?.key}" is required`);
}
if (params.members?.value && !Array.isArray(params.members.value)) {
throw new Error(`Param "${params.members.key}" must be an array if provided`);
}
if (params.customFields?.value && !(typeof params.customFields.value === 'object')) {
throw new Error(`Param "${params.customFields.key}" must be an object if provided`);
}
if (params.teams?.value && !Array.isArray(params.teams.value)) {
throw new Error(`Param ${params.teams.key} must be an array`);
}
}
async function createChannel(
userId: string,
params: {
name?: string;
members?: string[];
customFields?: Record<string, any>;
extraData?: Record<string, any>;
readOnly?: boolean;
excludeSelf?: boolean;
},
): Promise<{ channel: IRoom }> {View on GitHub (pinned to f9d3ec372b)
Solutions
- Send customFields as a JSON object: customFields: { department: 'eng' }.
- If the client must send a string, parse it server-side before this check.
- Audit for null: this guard lets null through - consider an explicit null exclusion upstream.
Example fix
// before - request body (double-serialized)
{ "name": "room", "customFields": "{\"dept\":\"eng\"}" }
// after
{ "name": "room", "customFields": { "dept": "eng" } } Defensive patterns
Strategy: validation
Validate before calling
function normalizeCustomFields(customFields) {
if (customFields == null) return undefined;
if (typeof customFields === 'string') {
// caller double-serialized - parse once
return JSON.parse(customFields);
}
if (typeof customFields === 'object' && !Array.isArray(customFields)) return customFields;
throw new Error('customFields must be a plain object');
} Type guard
function isPlainObject(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
} Try / catch
try {
await api.createChannel({ name, customFields });
} catch (e) {
if (/must be an object/.test(e.message)) {
customFields = typeof customFields === 'string' ? JSON.parse(customFields) : {};
return api.createChannel({ name, customFields });
}
throw e;
} Prevention
- Send customFields as a JSON object, not a stringified one.
- Exclude arrays and null explicitly - the server's typeof check lets both through.
- Validate the shape client-side with a schema.
When it happens
Trigger: Submitting customFields as a JSON string (e.g., customFields: '{"k":"v"}') instead of an object, or as a primitive value.
Common situations: Double-serialized JSON (client JSON.stringify-ing customFields then the transport stringifying again); passing customFields: null trips typeof null === 'object' so it passes (latent bug); form libraries that coerce objects to strings.
Related errors
- Param "${params.members.key}" must be an array if provided
- Param ${params.teams.key} must be an array
- Param "${params.name?.key}" is required
- error-room-not-found
- unauthorized
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/08500d6c95417632.
Report an issue: GitHub.