Bug Description
createMany operations fail with an Invariant failed error when the @zenstackhq/plugin-policy is enabled and models have access control policies involving nested relation traversals.
Switching to individual create calls in a loop resolves the issue. This occurs on multiple models, not just a single one.
Environment
- ZenStack version:
@zenstackhq/orm ^3.3.3, @zenstackhq/plugin-policy ^3.3.3, @zenstackhq/cli ^3.3.3
- Prisma version: 7.3.0
- Database: PostgreSQL
- Node.js: v22
- Integration: Using
zenstack-trpc (^0.1.22) to auto-generate CRUD routes
Schema (minimal reproduction)
The policy plugin is enabled:
plugin policy {
provider = '@zenstackhq/plugin-policy'
}
Here is a simplified version of the models involved. The key aspect is that access policies traverse multiple nested relations:
model HomePage {
id String @id @default(cuid())
projectId String @unique
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
cards HomeCard[]
@@allow('all', auth().role == SUPER_ADMIN)
@@allow('all', auth().role == PARTNER_ADMIN && project.organization.partnerManagers?[userId == auth().id])
@@allow('all', auth().role == ORG_ADMIN && auth().organizationId == project.organizationId)
@@allow('all', auth().role == USER && auth().organizationId == project.organizationId)
}
model HomeCard {
id String @id @default(cuid())
title TranslatedField[] @json
subtitle TranslatedField[] @json
icon String?
mainColor String?
order Int @default(0)
type HomeCardType @default(AGENT)
homePageId String
homePage HomePage @relation(fields: [homePageId], references: [id], onDelete: Cascade)
// Policies traverse deeply nested relations: homeCard -> homePage -> project -> organization -> partnerManagers
@@allow('all', auth().role == SUPER_ADMIN)
@@allow('all', auth().role == PARTNER_ADMIN && homePage.project.organization.partnerManagers?[userId == auth().id])
@@allow('all', auth().role == ORG_ADMIN && auth().organizationId == homePage.project.organizationId)
@@allow('all', auth().role == USER && auth().organizationId == homePage.project.organizationId)
}
The Project model links to Organization, which has a partnerManagers relation (a join table with userId and organizationId). So the PARTNER_ADMIN policy on HomeCard traverses 4 levels of relations: homePage.project.organization.partnerManagers?[...].
Steps to Reproduce
- Enable the policy plugin
- Define models with
@@allow policies that traverse multiple nested relations (3-4 levels deep)
- Call
createMany on one of these models (e.g., via the auto-generated tRPC/REST endpoint)
Error
POST /generated/homeCard/createMany → 400 Bad Request
{
"error": {
"message": "Failed to execute query: Error: Invariant failed",
"code": "INTERNAL_SERVER_ERROR"
}
}
The request body contains a standard createMany payload with an array of data objects (scalar fields only, no nested relations).
Workaround
Replacing createMany with individual create calls in a loop works without any issues:
// This fails:
await db.homeCard.createMany({ data: items });
// This works:
for (const item of items) {
await db.homeCard.create({ data: item });
}
Additional Context
- The issue is not specific to the HomeCard model — it happens on other models with similar deeply nested relation policies as well.
- The error message
Invariant failed is non-descriptive and makes debugging difficult. A more specific error message would be very helpful.
- Based on issue #1712, ZenStack V2 internally translated
createMany to individual create calls when access policies are involved. It's possible this translation has a bug in V3 when policies involve deep relation traversals.
- The authenticated user context is properly set (individual
create calls work fine with the same auth context).
Bug Description
createManyoperations fail with anInvariant failederror when the@zenstackhq/plugin-policyis enabled and models have access control policies involving nested relation traversals.Switching to individual
createcalls in a loop resolves the issue. This occurs on multiple models, not just a single one.Environment
@zenstackhq/orm^3.3.3,@zenstackhq/plugin-policy^3.3.3,@zenstackhq/cli^3.3.3zenstack-trpc(^0.1.22) to auto-generate CRUD routesSchema (minimal reproduction)
The policy plugin is enabled:
Here is a simplified version of the models involved. The key aspect is that access policies traverse multiple nested relations:
The
Projectmodel links toOrganization, which has apartnerManagersrelation (a join table withuserIdandorganizationId). So the PARTNER_ADMIN policy on HomeCard traverses 4 levels of relations:homePage.project.organization.partnerManagers?[...].Steps to Reproduce
@@allowpolicies that traverse multiple nested relations (3-4 levels deep)createManyon one of these models (e.g., via the auto-generated tRPC/REST endpoint)Error
The request body contains a standard
createManypayload with an array of data objects (scalar fields only, no nested relations).Workaround
Replacing
createManywith individualcreatecalls in a loop works without any issues:Additional Context
Invariant failedis non-descriptive and makes debugging difficult. A more specific error message would be very helpful.createManyto individualcreatecalls when access policies are involved. It's possible this translation has a bug in V3 when policies involve deep relation traversals.createcalls work fine with the same auth context).