payloadcms/payload · error · APIError
Invalid ID value in ${JSON.stringify(queryValue)}
Error message
Invalid ID value in ${JSON.stringify(queryValue)} What it means
`APIError` from `parseParams` in the optimized `in`/`not_in` path on the `id` field when `adapter.limitedBoundParameters` is set: every element of the array value must be a number, `null`, or a valid string ID (matching `/^[\w-]+$/`). If any element is another type or a string with invalid characters, the raw-SQL interpolation path is unsafe and the query is rejected with a 400.
Source
Thrown at packages/drizzle/src/queries/parseParams.ts:491
let isInvalid = false
for (const val of queryValue) {
if (typeof val === 'number' || val === null) {
continue
}
if (typeof val === 'string') {
if (!isValidStringID(val)) {
isInvalid = true
break
} else {
continue
}
}
isInvalid = true
break
}
if (isInvalid) {
throw new APIError(`Invalid ID value in ${JSON.stringify(queryValue)}`)
}
constraints.push(
sql.raw(
`"${getTableName(resolvedColumn.table)}"."${resolvedColumn.name}" ${operator === 'in' ? 'IN' : 'NOT IN'} (${queryValue
.map((e) => {
if (e === null) {
return `NULL`
}
if (typeof e === 'number') {
return e
}
return `'${e}'`
})
.join(',')})`,
),View on GitHub (pinned to 00c58b35c0)
Solutions
- Map the array to primitive IDs before querying: `ids.map(x => typeof x === 'object' ? x.id : x)`.
- Validate each string ID with `/^[\w-]+$/` (or `isValidStringID`) and strip/reject invalid entries.
- Drop non-numeric, non-null, non-string values from the array before sending the query.
- If IDs legitimately contain other characters, use a non-id field or disable the limited-bound-parameters path per the adapter docs.
Example fix
// before
await payload.find({ collection: 'posts', where: { id: { in: [1, '2', { id: 3 }, 'a b'] } } })
// after
const raw = [1, '2', someObj.id, 'a-b'] // extract ids, sanitize strings
const ids = raw.filter(v => typeof v === 'number' || v === null || (typeof v === 'string' && /^\w-$/.test(v)))
await payload.find({ collection: 'posts', where: { id: { in: ids } } }) Defensive patterns
Strategy: validation
Validate before calling
function isValidStringID(value) { return /^[\w-]+$/.test(value) }
function sanitizeIdArray(arr) {
return arr.filter(v =>
v === null ||
typeof v === 'number' ||
(typeof v === 'string' && isValidStringID(v))
)
}
const ids = sanitizeIdArray(rawIds)
await payload.find({ collection, where: { id: { in: ids } } }) Type guard
const isValidIdValue = (v): boolean => v === null || typeof v === 'number' || (typeof v === 'string' && /^\w-$/.test(v))
Try / catch
try {
await payload.find({ collection, where: { id: { in: ids } } })
} catch (err) {
if (err?.statusCode === 400 && /Invalid ID value in/.test(err?.message)) {
return res.status(400).json({ error: 'One or more IDs are malformed.' })
}
throw err
} Prevention
- Extract raw IDs from objects before building in/not_in arrays.
- Validate string IDs against /^[\w-]+$/ before sending queries.
- Drop non-numeric, non-null, non-string entries from id arrays.
- Sanitize client-supplied id lists at the API boundary.
When it happens
Trigger: Calling `payload.find({ collection, where: { id: { in: [...] } } })` (or `not_in`) on an adapter with `limitedBoundParameters`, where the array contains an object, boolean, symbol, or a string that fails `isValidStringID` (spaces, punctuation, etc.).
Common situations: Passing objects (`{ id: 1 }`) instead of raw IDs into an `in` list; IDs containing characters outside `[A-Za-z0-9_-]`; mixing types in an `in` array; client form data feeding unparsed values into an id query.
Related errors
- ${value} is not allowed as a JSON query value
- The following path cannot be queried: ${relationOrPath}
- The following path${results.length === 1 ? '' : 's'} cannot
- Either collection or globalSlug must be passed.
- Collection with the slug ${collectionSlug} was not found.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/5f7eb067a5e9a771.
Report an issue: GitHub.