payloadcms/payload · error · APIError
${value} is not allowed as a JSON query value
Error message
${value} is not allowed as a JSON query value What it means
A 400 `APIError` from `sanitizeValue` inside `createJSONQuery`: a query value used against a JSON/JSONB column (arrays, `json`, `richText`, localized JSON paths) failed the strict `SAFE_STRING_REGEX` (`/^[\w @.\-+:]*$/`). The regex is an allow-list that prevents SQL/JSONPath injection when interpolating values into raw `jsonb_path_exists(...)` SQL, so anything outside word chars, space, `@ . - + :` is rejected.
Source
Thrown at packages/drizzle/src/postgres/createJSONQuery/index.ts:32
not_in: 'in',
not_like: '!like_regex',
}
const sanitizeValue = (value: unknown, operator?: string): string => {
if (value === null) {
return `NULL`
}
if (typeof value === 'number' || typeof value === 'boolean') {
return `${value}`
}
if (typeof value !== 'string') {
throw new Error('Invalid value type')
}
if (!SAFE_STRING_REGEX.test(value)) {
throw new APIError(`${value} is not allowed as a JSON query value`, 400)
}
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
const prefix = ['like', 'not_like'].includes(operator ?? '') ? '(?i)' : ''
return `"${prefix}${escaped}"`
}
export const createJSONQuery = ({ column, operator, pathSegments, value }: CreateJSONQueryArgs) => {
const columnName = typeof column === 'object' ? column.name : column
const jsonPaths = pathSegments
.slice(1)
.map((key) => {
return `${sanitizePathSegment(key)}[*]`
})
.join('.')
View on GitHub (pinned to 00c58b35c0)
Solutions
- Sanitize/strip user input to safe characters before sending it as a `where` query value against array/json fields.
- For free-text search, prefer a dedicated text/textarea field (parameterized `like`) instead of querying raw JSON.
- If the value legitimately contains punctuation, perform the filter in application code after fetching, or store the searchable value in a normal column.
- If you need accent-insensitive matching on text, use the `unaccent` operator handler against a text column rather than a JSON path.
Example fix
// before: payload.find({ collection: 'pages', where: { 'meta.tags': { contains: 'C++ (API)' } } })
// -> throws '<value> is not allowed as a JSON query value'
// after: strip unsafe characters, or move tags to a relationship/text field
const safe = userInput.replace(/[^\w @.\-+:]/g, '')
await payload.find({ collection: 'pages', where: { 'meta.tags': { contains: safe } } }) Defensive patterns
Strategy: validation
Validate before calling
// Validate JSON-query values against the same allow-list before sending
const SAFE = /^[\w @.\-+:]*$/
function safeJsonValue(v) {
if (v === null || typeof v === 'number' || typeof v === 'boolean') return v
if (typeof v === 'string') {
if (!SAFE.test(v)) throw new Error(`Unsafe JSON query value: ${v}`)
return v
}
throw new Error('Invalid JSON query value type')
}
const clean = safeJsonValue(userInput)
await payload.find({ collection, where: { 'meta.tags': { contains: clean } } }) Type guard
const isSafeJsonQueryString = (v: unknown): v is string => typeof v === 'string' && /^[\w @.\-+:]*$/.test(v)
Try / catch
try {
await payload.find({ collection, where: { 'meta.tags': { contains: q } } })
} catch (err) {
if (err?.statusCode === 400 && /not allowed as a JSON query value/.test(err.message)) {
return res.status(400).json({ error: 'Search contains unsupported characters.' })
}
throw err
} Prevention
- Sanitize free-text input before using it in array/json where clauses.
- Prefer searchable text columns over raw JSON for user-facing search.
- Document the allow-list character set for API consumers querying JSON fields.
When it happens
Trigger: Querying an array/json field with `contains`/`like`/`equals`/`in` and a value containing punctuation or symbols not in the allow-list — e.g. quotes, parentheses, commas, `<`, `&`, emoji, or accented/non-ASCII letters. Triggers through the Payload REST/local API `where` query on array/JSON fields when `pathSegments.length > 1` (nested JSON path).
Common situations: Free-text search boxes feeding `contains` against an array/json field with user input containing punctuation; querying richText; localized JSON field queries with special characters; data containing accented characters that the ASCII-only word class rejects.
Related errors
- Invalid ID value in ${JSON.stringify(queryValue)}
- ${value} is not allowed as a JSON query value
- Either collection or globalSlug must be passed.
- Collection with the slug ${collectionSlug} was not found.
- Relationship field was not found
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/d9d9d81553b28242.
Report an issue: GitHub.