payloadcms/payload · error · InvalidFieldRelationship
Field ${field.label} has invalid relationship '${relationshi
Error message
Field ${field.label} has invalid relationship '${relationship}'. What it means
A relationship/upload field's `relationTo` slug is not in the `validRelationships` allow-list passed during sanitization. `validRelationships` is non-null when Payload restricts which collections a relationship may target (notably for relationships inside arrays/blocks/uploads and during cross-collection validation), so any target outside that list is rejected.
Source
Thrown at packages/payload/src/fields/config/sanitize.ts:264
parentIsLocalized,
polymorphicJoins,
})
}
// Relationship/upload validation
if (field.type === 'relationship' || field.type === 'upload') {
if (Array.isArray(field.relationTo) && field.relationTo.length === 0) {
throw new Error(
`Field "${field.name}" of type "${field.type}" has an empty relationTo array. At least one collection must be specified.`,
)
}
if (validRelationships) {
const relationships = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo]
relationships.forEach((relationship: string) => {
if (!validRelationships.includes(relationship)) {
throw new InvalidFieldRelationship(field, relationship)
}
})
}
}
// Upload isSortable default
if (field.type === 'upload') {
if (!field.admin || !('isSortable' in field.admin)) {
field.admin = {
isSortable: true,
...field.admin,
}
}
}
// Slug field: apply defaults, attach generation hook, expose slugify to the server fn.
if (field.type === 'slug') {
const useAsSlug = field.useAsSlugView on GitHub (pinned to 00c58b35c0)
Solutions
- Correct the `relationTo` slug so it matches an existing, allowed collection.
- If the collection genuinely should be allowed, review why `validRelationships` is scoped (e.g. upload-file relationships) and adjust the container config.
- Ensure the referenced collection is declared earlier or that sanitization order resolves it.
Example fix
// before (Posts collection does not exist or is not allowed here)
{ type: 'relationship', name: 'post', relationTo: 'Post' }
// after
{ type: 'relationship', name: 'post', relationTo: 'posts' } Defensive patterns
Strategy: validation
Validate before calling
function assertRelationshipsValid(config) {
const allSlugs = new Set(config.collections.map(c => c.slug))
const problems = []
function walk(fields) {
for (const f of fields) {
if (f?.type === 'relationship' || f?.type === 'upload') {
const rels = Array.isArray(f.relationTo) ? f.relationTo : [f.relationTo]
for (const r of rels) if (!allSlugs.has(r)) problems.push(`${f.name} -> ${r}`)
}
if (Array.isArray(f?.fields)) walk(f.fields)
if (Array.isArray(f?.blocks)) for (const b of f.blocks) if (b?.fields) walk(b.fields)
}
}
for (const c of config.collections) walk(c.fields)
return problems
} Type guard
function relationToExists(f: any, knownSlugs: Set<string>): boolean {
if (f?.type !== 'relationship' && f?.type !== 'upload') return true
const rels = Array.isArray(f.relationTo) ? f.relationTo : [f.relationTo]
return rels.every((r: string) => knownSlugs.has(r))
} Try / catch
try {
await payload.init({ config })
} catch (err) {
if (err?.name === 'InvalidFieldRelationship' || /invalid relationship/i.test(err?.message ?? '')) {
console.error('Bad relationship target:', err?.data)
}
throw err
} Prevention
- Centralize collection slugs as constants and reference them in `relationTo` to avoid typos.
- Re-run the relationship lint after renaming or removing any collection.
When it happens
Trigger: A relationship field nested in an array/block pointing to a collection not allowed in that context; a relationship whose `relationTo` slug has a typo or references a not-yet-defined collection when validRelationships is scoped.
Common situations: Renaming a collection without updating references; pointing an array's relationship at a collection that isn't in the allowed set for that container; copy-pasting a relationship across configs with different collection sets.
Related errors
- Field "${field.name}" of type "${field.type}" has an empty r
- Field ${field.label} has reserved name '${fieldName}'.
- Virtual field ${virtualField.name} in ${globalConfig ? `glob
- Invalid join field ${field.name}. The config does not have a
- File type '${file.mimetype}' is not allowed.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/205ee7363caaed53.
Report an issue: GitHub.