nodejs/node · error · InvalidArgumentError
UND_ERR_INVALID_ARG
UND_ERR_INVALID_ARG
Error message
options.operator must to be a case insensitive string equal to 'OR' or 'AND'
What it means
Thrown by the mock call-history filter logic (`InvalidArgumentError`, code `UND_ERR_INVALID_ARG`) when `options.operator` is present in a `filterCalls` criteria-options object but is not a case-insensitive `'OR'` or `'AND'`. The operator controls how multiple criteria keys (protocol, host, path, method, etc.) are combined: `OR` returns the union (default), `AND` returns only logs matching every criterion. Validation runs in `buildAndValidateFilterCallsOptions` before filtering. There is a second, near-identical throw inside `handleFilterCallsWithOptions` that exists only as a defensive guard and should be unreachable if validation ran.
Source
Thrown at deps/undici/src/lib/mock/mock-call-history.js:25
switch (options.operator) {
case 'OR':
store.push(...handler(criteria, allLogs))
return store
case 'AND':
return handler(criteria, store)
default:
// guard -- should never happens because buildAndValidateFilterCallsOptions is called before
throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \'OR\' or \'AND\'')
}
}
function buildAndValidateFilterCallsOptions (options = {}) {
const finalOptions = {}
if ('operator' in options) {
if (typeof options.operator !== 'string' || (options.operator.toUpperCase() !== 'OR' && options.operator.toUpperCase() !== 'AND')) {
throw new InvalidArgumentError('options.operator must to be a case insensitive string equal to \'OR\' or \'AND\'')
}
return {
...finalOptions,
operator: options.operator.toUpperCase()
}
}
return finalOptions
}
function makeFilterCalls (parameterName) {
return (parameterValue, logs = this.logs) => {
if (typeof parameterValue === 'string' || parameterValue == null) {
return logs.filter((log) => {
return log[parameterName] === parameterValue
})
}View on GitHub (pinned to 1b2de5e052)
Solutions
- Use `operator: 'OR'` (default, union semantics) or `operator: 'AND'` (intersection semantics), case-insensitive.
- Omit the option entirely to get the default `OR` behavior.
- If you need richer boolean logic, post-filter the returned logs in your own code.
Example fix
// before
history.filterCalls({ path: '/x', method: 'GET' }, { operator: 'XOR' })
history.filterCalls({ path: '/x' }, { operator: true })
// after
history.filterCalls({ path: '/x', method: 'GET' }, { operator: 'AND' })
history.filterCalls({ path: '/x' }, { operator: 'OR' })
history.filterCalls({ path: '/x' }) // default OR Defensive patterns
Strategy: validation
Validate before calling
function validateOperator(operator) {
if (operator == null) return 'OR'
if (typeof operator !== 'string') throw new Error('operator must be a string')
const up = operator.toUpperCase()
if (up !== 'OR' && up !== 'AND') throw new Error('operator must be OR or AND')
return up
} Type guard
function isFilterOperator(v) {
return typeof v === 'string' && ['OR', 'AND'].includes(v.toUpperCase())
} Prevention
- Use only 'OR'/'AND' (case-insensitive) or omit the option for OR.
- Do post-filtering for richer boolean logic.
When it happens
Trigger: Calling `callHistory.filterCalls(criteria, { operator: 'XOR' })`, `{ operator: 'or' }` is fine (case-insensitive), but `{ operator: 'NOT' }`, `{ operator: true }`, or `{ operator: '' }` throws. The guard is `typeof options.operator !== 'string' || (toUpperCase !== 'OR' && toUpperCase !== 'AND')`.
Common situations: Assuming additional boolean operators are supported; passing a lowercased value works but `operator: 'AND'`/`'OR'` only; passing a non-string from config; reaching the unreachable guard via a code path that skips validation (internal misuse).
Related errors
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/36a602a762e9c6eb.
Report an issue: GitHub.