payloadcms/payload · warning · APIError
Cannot findDistinct by a virtual field that isn't linked to
Error message
Cannot findDistinct by a virtual field that isn't linked to a relationship field.
What it means
In `findDistinctOperation`, if the resolved field is `virtual` (truthy) but `virtual` is not a string (i.e. `true` or a boolean), it throws `APIError`. Distinct on virtual fields only works when the virtual field declares a relationship path as a string; a bare boolean virtual has no backing relationship to deduplicate against.
Source
Thrown at packages/payload/src/collections/operations/findDistinct.ts:156
const hasAccess = await fieldResult.field.access.read({
collection: collectionConfig,
req,
})
if (!hasAccess) {
throw new Forbidden(req.t)
}
}
await validateSortQuery({
collectionConfig,
overrideAccess: overrideAccess!,
req,
sort: args.sort,
})
if ('virtual' in fieldResult.field && fieldResult.field.virtual) {
if (typeof fieldResult.field.virtual !== 'string') {
throw new APIError(
`Cannot findDistinct by a virtual field that isn't linked to a relationship field.`,
)
}
let relationPath: string = ''
let currentFields: FlattenedField[] = collectionConfig.flattenedFields
const fieldPathSegments = fieldResult.field.virtual.split('.')
for (const segment of fieldResult.field.virtual.split('.')) {
relationPath = `${relationPath}${segment}`
fieldPathSegments.shift()
const field = currentFields.find((e) => e.name === segment)!
if (
(field.type === 'relationship' || field.type === 'upload') &&
typeof field.relationTo === 'string'
) {
break
}
if ('flattenedFields' in field) {View on GitHub (pinned to 00c58b35c0)
Solutions
- Configure the virtual field with a string relationship path (`virtual: 'status.label'`), or
- Choose a non-virtual field for the distinct query.
Example fix
// before
{ name: 'statusLabel', type: 'text', virtual: true }
// after
{ name: 'statusLabel', type: 'text', virtual: 'status.label' } Defensive patterns
Strategy: validation
Validate before calling
function isDistinctableVirtual(field) {
return !('virtual' in field) || !field.virtual || typeof field.virtual === 'string'
} Type guard
function isStringVirtual(field): boolean {
return typeof field?.virtual === 'string'
} Prevention
- Do not run distinct on boolean-virtual fields.
- Configure virtuals with relationship paths (`virtual: 'rel.sub'`) when distinct is needed.
When it happens
Trigger: Calling findDistinct on a field configured with `virtual: true` (boolean) rather than `virtual: 'relation.subField'` (string path).
Common situations: Misconfigured virtual field; attempting distinct on a computed field with no relationship backing.
Related errors
- Invalid template given
- Invalid database type given. Valid types are: ${Object.value
- No package.json found in this project
- Payload is not installed in this project
- Could not find ExportDefaultDeclaration in next.config.js
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/b79ddc8cdb8a51ce.
Report an issue: GitHub.