payloadcms / payloadcms/payload
Virtual field fails to resolve when referencing a related field populated via afterRead hook
@GermanJablo is already working on this.
Since Mar 16, 2026.
- Dominant language
- TypeScript
- Stars
- 44.8k
- Forks
- 4.2k
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 53
Description
Describe the Bug
A virtual field using dot-notation (e.g., virtual: "relation.field") fails to populate with data if the target field in the related collection is itself a virtual/hook-populated field generated during afterRead.
In this scenario, the parent collection's virtual field returns undefined or an empty string, likely because the virtual field resolution occurs before the afterRead hooks of the populated relationship have finished executing.
Expected Behaviour
The virtual field authorName should correctly display the value generated by the afterRead hook of the related author document.
Actual Behaviour
The field remains empty. If the fullName field in the Users collection is changed to a standard stored text field (no hooks), the virtual field works as expected.
Link to the code that reproduces this issue
https://github.com/max-atkins/payload-virtual-field-blank
Reproduction Steps
- Create a Users collection with a fullName field that uses an afterRead hook to combine firstName and lastName.
- Create a Posts collection with a relationship field to Users.
- Add a virtual field to Posts named authorName with virtual: "author.fullName".
- Create a User, then create a Post linked to that User.
- Observe that authorName in the Admin UI or API response is empty/null, despite author.fullName being visible when inspecting the expanded relationship.
{
slug: "users",
fields: [
{ name: "firstName", type: "text", required: true },
{ name: "lastName", type: "text", required: true },
{
name: "fullName",
type: "text",
admin: { readOnly: true },
hooks: {
afterRead: [({ data }) => `${data.firstName} ${data.lastName}`], // Simplified for example
},
},
],
}
{
slug: "posts",
fields: [
{
name: "author",
type: "relationship",
relationTo: "users",
},
{
name: "authorName",
type: "text",
virtual: "author.fullName",
admin: { readOnly: true },
}
]
}
Which area(s) are affected?
area: core
Environment Info
Binaries:
Node: 24.11.0
npm: 11.6.1
Yarn: 1.22.22
pnpm: 10.28.0
Relevant Packages:
payload: 3.79.0
next: 16.1.6
@payloadcms/db-postgres: 3.79.0
@payloadcms/drizzle: 3.79.0
@payloadcms/graphql: 3.79.0
@payloadcms/live-preview: 3.79.0
@payloadcms/live-preview-react: 3.79.0
@payloadcms/next/utilities: 3.79.0
@payloadcms/plugin-cloud-storage: 3.79.0
@payloadcms/plugin-multi-tenant: 3.79.0
@payloadcms/richtext-lexical: 3.79.0
@payloadcms/storage-s3: 3.79.0
@payloadcms/translations: 3.79.0
@payloadcms/ui/shared: 3.79.0
react: 19.2.3
react-dom: 19.2.3
Operating System:
Platform: linux
Arch: x64
Version: #14~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 15 15:52:10 UTC 2
Available memory (MB): 31776
Available CPU cores: 32
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.