Description and expected behavior
I think there is a bug or limitation in the ZenStack policy plugin’s SQL rewriting for computed fields—especially with complex subqueries or correlated subqueries in SQLite.
The computed field SQL for [total], [subTotal] etc., is valid on its own.
When you use the policy-wrapped client ([db] with [PolicyPlugin]), ZenStack rewrites the query to enforce row-level security.
This rewriting sometimes produces malformed SQL (e.g., stray or mismatched braces, or invalid subquery placement) that SQLite cannot parse, resulting in errors like:
SqliteError: unrecognized token: "}"
Evidence:
The same computed field works with the raw client ([rawDb])) but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.
total: (qb, { modelAlias }) =>
sqlCOALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0),
subTotal: (qb, { modelAlias }) =>
sql( COALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0) * (1 + COALESCE(${sql.ref(${modelAlias}.tax)}, 0) / 100.0) ) - COALESCE(${sql.ref(${modelAlias}.discount)}, 0),
Environment (please complete the following information):
- ZenStack version: [e.g., 3..6.4]
- Database type: [e.g. Sqlite]
- Node.js/Bun version: [e.g., v25.8.2]
- Package manager: [pnpm]
Additional context
Evidence:
The same computed field works with the raw client (rawDb but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.
What to do:
Report this to the ZenStack maintainers with a minimal reproduction (computed field, policy, and failing query).
As a workaround, avoid selecting these computed fields through the policy-wrapped client, or compute them in route code as you’ve done.
Summary:
This is not a bug in your SQL or schema, but a ZenStack policy plugin SQL generation bug for complex computed fields in SQLite.
Description and expected behavior
I think there is a bug or limitation in the ZenStack policy plugin’s SQL rewriting for computed fields—especially with complex subqueries or correlated subqueries in SQLite.
The computed field SQL for [total], [subTotal] etc., is valid on its own.
When you use the policy-wrapped client ([db] with [PolicyPlugin]), ZenStack rewrites the query to enforce row-level security.
This rewriting sometimes produces malformed SQL (e.g., stray or mismatched braces, or invalid subquery placement) that SQLite cannot parse, resulting in errors like:
SqliteError: unrecognized token: "}"
Evidence:
The same computed field works with the raw client ([rawDb])) but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.
total: (qb, { modelAlias }) =>
sql
COALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0),subTotal: (qb, { modelAlias }) =>
sql
( COALESCE(( SELECT SUM((("WorkItem"."budgetedPrice" * "WorkItem"."quantity") * (1 + COALESCE("WorkItem"."profit", 0) / 100.0)) - COALESCE("WorkItem"."adjustment", 0)) FROM "WorkItem" INNER JOIN "WorkOrder" ON "WorkItem"."workOrderId" = "WorkOrder"."id" WHERE "WorkOrder"."projectId" = ${sql.ref(${modelAlias}.projectId)} ), 0) * (1 + COALESCE(${sql.ref(${modelAlias}.tax)}, 0) / 100.0) ) - COALESCE(${sql.ref(${modelAlias}.discount)}, 0),Environment (please complete the following information):
Additional context
Evidence:
The same computed field works with the raw client (rawDb but fails with the policy-wrapped client.
The error is not a TypeScript or schema error, but a runtime SQL parse error after ZenStack’s policy transformation.
What to do:
Report this to the ZenStack maintainers with a minimal reproduction (computed field, policy, and failing query).
As a workaround, avoid selecting these computed fields through the policy-wrapped client, or compute them in route code as you’ve done.
Summary:
This is not a bug in your SQL or schema, but a ZenStack policy plugin SQL generation bug for complex computed fields in SQLite.