n8n-io/n8n · error · ExpressionExtensionError
argument of keepFieldsContaining must be a non-empty string
Error message
argument of keepFieldsContaining must be a non-empty string
What it means
Twin of 333: the object extension `keepFieldsContaining(match)` retains only fields whose string value contains `match`, deleting the rest. The same guard rejects a non-string or empty `match` — an empty match would (correctly but uselessly) keep everything, and a non-string match cannot substring-test.
Source
Thrown at packages/@n8n/expression-runtime/src/extensions/object-extensions.ts:54
function removeFieldsContaining(value: object, extraArgs: string[]): object {
const [match] = extraArgs;
if (typeof match !== 'string' || match === '') {
throw new ExpressionExtensionError('removeFieldsContaining(): expected non-empty string arg');
}
const newObject = { ...value };
for (const [key, val] of Object.entries(value)) {
if (typeof val === 'string' && val.includes(match)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
delete (newObject as any)[key];
}
}
return newObject;
}
function keepFieldsContaining(value: object, extraArgs: string[]): object {
const [match] = extraArgs;
if (typeof match !== 'string' || match === '') {
throw new ExpressionExtensionError(
'argument of keepFieldsContaining must be a non-empty string',
);
}
const newObject = { ...value };
for (const [key, val] of Object.entries(value)) {
if (typeof val !== 'string' || (typeof val === 'string' && !val.includes(match))) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
delete (newObject as any)[key];
}
}
return newObject;
}
export function compact(value: object): object {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newObj: any = {};
for (const [key, val] of Object.entries(value)) {
if (val !== null && val !== undefined && val !== 'nil' && val !== '') {View on GitHub (pinned to 5ac6606e81)
Solutions
- Pass a non-empty string match: `.keepFieldsContaining('email')`.
- Pre-validate the match value in a Code node.
- Coerce numerics with `.toString()` if a numeric substring match is intended.
Example fix
// before
{{ $json.row.keepFieldsContaining($json.q) }}
// after
{{ $json.row.keepFieldsContaining(String($json.q || '')) || $json.row }} Defensive patterns
Strategy: validation
Validate before calling
const match = $json.match;
if (typeof match !== 'string' || match === '') {
throw new Error('keepFieldsContaining(): match must be a non-empty string');
}
return $json; Type guard
const isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;
Prevention
- Pass a non-empty string match.
- Pre-validate dynamic match values upstream.
- Coerce numerics with `String(...)` when needed.
When it happens
Trigger: `.keepFieldsContaining('')` or `.keepFieldsContaining(null)` or `.keepFieldsContaining(42)`.
Common situations: Dynamic match value that is empty or the wrong type; field rename leaving `match` undefined; numeric ID used as a substring match.
Related errors
- removeFieldsContaining(): expected non-empty string arg
- intersection(): expected array arg, e.g. .intersection([1, 2
- keys and values must be arrays
- Unknown algorithm ${algorithm}. Available algorithms are: ${
- Unsupported unit '${String(errorUnit)}'. Supported: ${durati
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/5d1bff2045d49047.
Report an issue: GitHub.