Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Nov 7, 2025

Summary by CodeRabbit

  • New Features

    • Unified error model: a single ORMError with reason enum and richer diagnostic fields for clearer error reporting.
  • Documentation

    • README polished: heading case, reorganized setup flow, and explicit manual install/configuration steps.
  • Breaking Changes

    • Legacy error classes removed: update integrations/tests to check ORMError + reason rather than previous error types.
  • Tests

    • End-to-end and unit tests updated to assert on the new error shape and nested cause messages.

Copilot AI review requested due to automatic review settings November 7, 2025 04:14
@claude
Copy link

claude bot commented Nov 7, 2025

Claude finished @ymc9's task —— View job


PR Review: Error System Refactoring

I've completed a comprehensive review of this error system refactoring. This is a well-structured improvement that standardizes error handling across ZenStack.

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Analyze core error handling changes in errors.ts
  • Review new error processor implementation
  • Examine error handling across ORM components
  • Check policy plugin error handling changes
  • Review server API error handling updates
  • Analyze test coverage and new error tests
  • Look for potential security/performance issues
  • Provide comprehensive feedback and recommendations

✅ Strengths

1. Well-structured Error Hierarchy

  • Clear separation between ORMErrorReason and RejectedByPolicyReason enums
  • Comprehensive error types covering all major failure scenarios
  • Good use of TypeScript error constructor with proper inheritance from Error

2. Comprehensive Error Context

  • Rich error metadata (model, dbErrorCode, sql, sqlParams, etc.)
  • Policy-specific error reasons for better debugging
  • Proper error chaining with cause parameter

3. Consistent API Integration

  • Both REST and RPC APIs properly map ORM errors to appropriate HTTP status codes
  • Good separation of concerns with dedicated error handling methods
  • Proper logging integration

4. Good Test Coverage

  • New test file covers major error scenarios (validation, not found, policy rejection, DB errors)
  • Tests verify both error types and specific error codes

🔍 Areas for Improvement

1. Error Processor Implementation (packages/orm/src/client/executor/error-processor.ts:6-12)

export function getDbErrorCode(error: unknown): unknown | undefined {
    if (error instanceof Error && 'code' in error) {
        return error.code;
    } else {
        return undefined;
    }
}
  • Issue: Very basic implementation with @todo comment
  • Recommendation: Consider adding type-specific error code extraction for different database drivers (SQLite vs PostgreSQL have different error formats)

2. Generic Error Type for dbErrorCode (packages/orm/src/client/errors.ts:83)

public dbErrorCode?: unknown;
  • Issue: Using unknown type loses type safety benefits
  • Recommendation: Consider a union type like string | number or create specific types for each database driver

3. Security Considerations

  • SQL Exposure: Error objects include sql and sqlParams fields which could expose sensitive data in logs
  • Recommendation: Consider sanitizing or limiting SQL exposure in production environments
  • Database Error Details: Raw database errors might leak internal structure information

4. Error Message Consistency (packages/orm/src/client/crud/operations/base.ts)

  • Some error messages are hardcoded strings while others are parameterized
  • Consider using a consistent error message templating system

🚀 Performance Considerations

1. Error Creation Performance

  • Factory functions like createDBQueryError are efficient
  • No unnecessary object creation or string manipulation

2. Error Processing Overhead

  • The error processor function is lightweight
  • Policy error checking integrates well without significant overhead

🔒 Security Assessment

1. Information Disclosure Risks

  • Medium Risk: SQL queries and parameters exposed in error objects
  • Low Risk: Database error codes might reveal schema information
  • Mitigation: Consider environment-specific error detail levels

2. Error Handling Robustness

  • Good error boundaries prevent unhandled exceptions
  • Proper error chaining maintains context without losing information

📋 Minor Issues

1. Type Safety (packages/orm/src/client/errors.ts:122-128)

export function createDBQueryError(message: string, dbError: unknown, sql: string, parameters: readonly unknown[]) {
  • Consider adding generic constraints for better type safety

2. Documentation

  • The @todo comment in error processor should be addressed or tracked in issues

3. Deleted Test File

  • The removal of tests/e2e/orm/policy/migrated/todo-sample.test.ts should be explained in the PR description

🎯 Recommendations

  1. Immediate: Address the @todo in error processor or create an issue to track it
  2. Short-term: Consider implementing environment-specific error detail levels for security
  3. Long-term: Evaluate adding structured error codes for better programmatic error handling

Overall Assessment

This is a solid refactoring that significantly improves error handling consistency across ZenStack. The new error system is well-designed, properly tested, and integrates cleanly with existing APIs. The main concerns are around information disclosure and the incomplete error processor implementation.

Recommendation: ✅ Approve with minor follow-ups


@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Consolidates many legacy ORM error classes into a single ORMError/ORMErrorReason surface with factory creators; updates numerous throw sites, imports, server error handlers, policy plugin utilities, tests, and docs to use the new error model and adjust assertions accordingly.

Changes

Cohort / File(s) Summary
Documentation & ZModel
README.md, packages/language/res/stdlib.zmodel
Heading case and organization edits in README; doc text updated to reference “ORM Client” in stdlib attribute docs.
Core Error Infrastructure
packages/orm/src/client/errors.ts, packages/orm/src/client/executor/error-processor.ts
Introduces ORMError class, ORMErrorReason & RejectedByPolicyReason enums, and factory creators (createConfigError, createNotFoundError, createInvalidInputError, createDBQueryError, createRejectedByPolicyError, createNotSupportedError, createInternalError); adds getDbErrorCode. Removes many old exported error classes.
Client Entry & Impl
packages/orm/src/client/index.ts, packages/orm/src/client/client-impl.ts
Restricts re-exports to ORMError, ORMErrorReason, RejectedByPolicyReason; switches client implementation to use factory errors (createConfigError, createNotFoundError, etc.).
Query Executor & Utils
packages/orm/src/client/executor/zenstack-query-executor.ts, packages/orm/src/client/query-utils.ts
Replaces old error classes with factory-created ORMError instances; changes instanceof checks to ORMError; many throws now use createInternalError/createDBQueryError with contextual info.
CRUD Dialects & Operations
packages/orm/src/client/crud/dialects/*, packages/orm/src/client/crud/operations/*, packages/orm/src/client/crud/validator/*
Replaces throws/imports of QueryError, NotFoundError, InternalError, InputValidationError, RejectedByPolicyError with corresponding factory functions; updates messages and import lists across dialects, base ops, create/update/delete flows, validators, and utils.
Policy Plugin
packages/plugins/policy/src/expression-transformer.ts, packages/plugins/policy/src/policy-handler.ts, packages/plugins/policy/src/utils.ts
Adds createRejectedByPolicyError and createUnsupportedError; replaces legacy error throws with factory-created ORMErrors or invariant(false, ...); exports MutationQueryNode type; adjusts imports to new error utilities.
Server API Handlers
packages/server/src/api/rest/index.ts, packages/server/src/api/rpc/index.ts
Reworks REST/RPC error handling to dispatch on ORMError.reason (via ts-pattern) and map to HTTP responses; replaces previous ZenStackError-based branches with makeORMErrorResponse/handleORMError.
Test infra & E2E tests
packages/testtools/src/vitest-ext.ts, packages/server/test/api/rest.test.ts, tests/e2e/orm/client-api/*, tests/e2e/orm/policy/*, tests/e2e/orm/plugin-infra/*
Adjusts test helpers and assertions to assert ORMError + ORMErrorReason or inspect e.cause.message for DB constraint checks; replaces many instanceof checks and removed todo-sample.test.ts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Changes are widespread but follow a consistent pattern (class → ORMError + factories).
  • Review focus areas:
    • packages/orm/src/client/errors.ts (ORMError fields, factory signatures, db code extraction).
    • packages/orm/src/client/query-utils.ts and executor/zenstack-query-executor.ts for correct error wrapping/context propagation.
    • Server REST/RPC reason→status mapping to ensure all cases handled.

Possibly related PRs

Poem

🐰
From scattered errors I hop and compose,
One ORMError to tidy the rows.
Factories crafted, reasons aligned,
Tests now check causes I cleverly find.
A rabbit’s small refactor — neat, refined!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: revised error system' clearly and concisely summarizes the primary change: a comprehensive refactoring of the ORM error handling system from multiple specific error classes to a unified ORMError-centric API with factory functions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/error-system

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efbeb17 and 56e245b.

📒 Files selected for processing (4)
  • packages/orm/src/client/crud/operations/base.ts (32 hunks)
  • packages/orm/src/client/errors.ts (2 hunks)
  • packages/server/src/api/rpc/index.ts (3 hunks)
  • tests/e2e/orm/client-api/error-handling.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/orm/client-api/error-handling.test.ts
  • packages/orm/src/client/errors.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/server/src/api/rpc/index.ts (1)
packages/orm/src/client/errors.ts (1)
  • ORMError (66-104)
packages/orm/src/client/crud/operations/base.ts (1)
packages/orm/src/client/errors.ts (6)
  • createDBQueryError (122-129)
  • createInternalError (147-151)
  • createNotSupportedError (143-145)
  • createNotFoundError (110-114)
  • createInvalidInputError (116-120)
  • ORMError (66-104)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: claude-review
🔇 Additional comments (4)
packages/server/src/api/rpc/index.ts (3)

2-2: LGTM! Imports align with the new error system.

The addition of ORMError, ORMErrorReason, and match imports correctly supports the refactored error handling approach.

Also applies to: 5-5


158-159: LGTM! Error handling correctly updated.

The instance check and method call have been properly updated to use the new ORMError type, maintaining the existing error handling flow.


192-221: Past issue resolved! The match chain now executes correctly.

The addition of .otherwise(() => {}) at line 216 addresses the previous review concern—the match handlers now execute properly, ensuring that status codes and error metadata are set according to the error reason. The implementation correctly maps each ORMErrorReason to its corresponding HTTP status and metadata.

packages/orm/src/client/crud/operations/base.ts (1)

21-31: LGTM! Error system imports are complete.

The import changes correctly bring in all necessary error factory functions and types to support the new error system. The transition from legacy error classes to factory-based creation is well-structured.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request refactors the error handling system in the ZenStack ORM to use a unified ORMError class with reason codes instead of multiple specific error classes (like InputValidationError, NotFoundError, QueryError, etc.).

  • Introduces a new ORMError class with ORMErrorReason enum for categorizing errors
  • Replaces specific error classes with factory functions that create ORMError instances
  • Updates all error handling code across the ORM, plugins, server packages, and tests
  • Improves error information by adding fields like dbErrorCode, sql, sqlParams, and rejectedByPolicyReason
  • Updates documentation to use "ORM Client" terminology instead of "Prisma Client"

Reviewed Changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/orm/src/client/errors.ts Introduces new ORMError class, ORMErrorReason enum, and factory functions to replace old error class hierarchy
packages/orm/src/client/executor/error-processor.ts Adds utility to extract database error codes from driver errors
packages/orm/src/client/index.ts Updates exports to only expose ORMError, ORMErrorReason, and RejectedByPolicyReason
packages/orm/src/client/executor/zenstack-query-executor.ts Updates query executor to use new error creation functions and wrap database errors properly
packages/orm/src/client/query-utils.ts Replaces error throws with new factory functions
packages/orm/src/client/crud/operations/*.ts Updates create, update, and delete operations to use new error factory functions
packages/orm/src/client/crud/validator/index.ts Updates input validation to use createInvalidInputError
packages/orm/src/client/crud/dialects/*.ts Updates dialect implementations to use new error functions
packages/orm/src/client/client-impl.ts Updates client implementation to use new error types
packages/plugins/policy/src/*.ts Updates policy plugin to use new error system with local helper functions
packages/server/src/api/rest/index.ts Refactors REST API error handling to use pattern matching on ORMErrorReason
packages/server/src/api/rpc/index.ts Refactors RPC API error handling to use pattern matching on ORMErrorReason
packages/testtools/src/vitest-ext.ts Updates test matchers to check for ORMError with specific reasons instead of specific error classes
tests/e2e/**/*.test.ts Updates tests to use new error checking patterns and test matchers
packages/language/res/stdlib.zmodel Updates documentation from "Prisma Client" to "ORM Client"
README.md Improves heading capitalization for consistency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/orm/src/client/crud/operations/base.ts (1)

2128-2144: Preserve the model when throwing not-found errors

Here the first argument to createNotFoundError is supposed to be the model name, but passing 'No rows found' sets err.model to that string while the message falls back to a generic template. Downstream error responses will therefore report the wrong model and omit the intended detail. Pass the actual model (and the custom message if you need it) instead:

-            throw createNotFoundError('No rows found');
+            throw createNotFoundError(model, 'No rows found');
packages/orm/src/client/query-utils.ts (1)

16-177: Restore client-facing error classification

All of these guard paths used to raise a query-level error when the caller supplied an unknown model/field/relation. After this refactor they bubble up as ORMErrorReason.INTERNAL_ERROR, which our HTTP and RPC adapters convert into 500s and “unexpected error” messaging. That masks actionable user mistakes (bad model name, missing relation, etc.) and regresses client UX. Please switch these cases to createInvalidInputError (and import it) so they remain 4xx-style signals.

-import { createInternalError } from './errors';
+import { createInternalError, createInvalidInputError } from './errors';
@@
-        throw createInternalError(`Model "${model}" not found in schema`, model);
+        throw createInvalidInputError(`Model "${model}" not found in schema`, model);
@@
-            throw createInternalError(`Field "${field}" not found in model "${modelOrType}"`, modelOrType);
+            throw createInvalidInputError(
+                `Field "${field}" not found in model "${modelOrType}"`,
+                modelOrType,
+            );
@@
-            throw createInternalError(`Field "${field}" not found in type "${modelOrType}"`, modelOrType);
+            throw createInvalidInputError(
+                `Field "${field}" not found in type "${modelOrType}"`,
+                modelOrType,
+            );
@@
-    throw createInternalError(`Model or type "${modelOrType}" not found in schema`, modelOrType);
+    throw createInvalidInputError(`Model or type "${modelOrType}" not found in schema`, modelOrType);
@@
-        throw createInternalError(`Model "${model}" does not have ID field(s)`, model);
+        throw createInvalidInputError(`Model "${model}" does not have ID field(s)`, model);
@@
-        throw createInternalError(`Field "${relationField}" is not a relation`, model);
+        throw createInvalidInputError(`Field "${relationField}" is not a relation`, model);
@@
-            throw createInternalError(`Relation references not defined for field "${relationField}"`, model);
+            throw createInternalError(`Relation references not defined for field "${relationField}"`, model);
@@
-            throw createInternalError(`Opposite relation not defined for field "${relationField}"`, model);
+            throw createInvalidInputError(`Opposite relation not defined for field "${relationField}"`, model);
@@
-            throw createInternalError(`Field "${fieldDef.relation.opposite}" is not a relation`, model);
+            throw createInvalidInputError(`Field "${fieldDef.relation.opposite}" is not a relation`, model);
@@
-            throw createInternalError(`Relation fields not defined for field "${relationField}"`, model);
+            throw createInternalError(`Relation fields not defined for field "${relationField}"`, model);
@@
-            throw createInternalError(`Relation references not defined for field "${relationField}"`, model);
+            throw createInternalError(`Relation references not defined for field "${relationField}"`, model);
@@
-        throw createInternalError(`ID fields not defined for model "${model}"`, model);
+        throw createInvalidInputError(`ID fields not defined for model "${model}"`, model);

Feel free to keep createInternalError for the true schema corruption paths (missing references, etc.), but the user-facing validation failures need to map to INVALID_INPUT again.

🧹 Nitpick comments (4)
packages/testtools/src/vitest-ext.ts (3)

76-89: Update error message to reference ORMError.

Line 86 still mentions "NotFoundError" in the error message, but should reference "ORMError with reason NOT_FOUND" for consistency with the new error system.

Apply this diff:

         return {
-            message: () => `expected NotFoundError, got no error`,
+            message: () => `expected ORMError with reason NOT_FOUND, got no error`,
             pass: false,
         };

91-110: Update error message to reference ORMError.

Line 107 still mentions "PolicyError" in the error message, but should reference "ORMError with reason REJECTED_BY_POLICY" for consistency with the new error system.

Apply this diff:

         return {
-            message: () => `expected PolicyError, got no error`,
+            message: () => `expected ORMError with reason REJECTED_BY_POLICY, got no error`,
             pass: false,
         };

112-131: Update error message to reference ORMError.

Line 128 still mentions "InputValidationError" in the error message, but should reference "ORMError with reason INVALID_INPUT" for consistency with the new error system.

Apply this diff:

         return {
-            message: () => `expected InputValidationError, got no error`,
+            message: () => `expected ORMError with reason INVALID_INPUT, got no error`,
             pass: false,
         };
packages/orm/src/client/errors.ts (1)

86-128: Populate dbErrorMessage when wrapping driver errors

ORMError.dbErrorMessage is documented as “The error message given by the underlying database driver,” but createDBQueryError never sets it, so consumers always see undefined. Please capture the driver message alongside the code.

 export function createDBQueryError(message: string, dbError: unknown, sql: string, parameters: readonly unknown[]) {
     const error = new ORMError(ORMErrorReason.DB_QUERY_ERROR, message, { cause: dbError });
     error.dbErrorCode = getDbErrorCode(dbError);
+    if (dbError instanceof Error) {
+        error.dbErrorMessage = dbError.message;
+    } else if (dbError != null) {
+        error.dbErrorMessage = String(dbError);
+    }
     error.sql = sql;
     error.sqlParams = parameters;
     return error;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2946115 and efbeb17.

📒 Files selected for processing (35)
  • README.md (3 hunks)
  • packages/language/res/stdlib.zmodel (1 hunks)
  • packages/orm/src/client/client-impl.ts (3 hunks)
  • packages/orm/src/client/crud/dialects/base-dialect.ts (8 hunks)
  • packages/orm/src/client/crud/dialects/postgresql.ts (2 hunks)
  • packages/orm/src/client/crud/dialects/sqlite.ts (3 hunks)
  • packages/orm/src/client/crud/operations/base.ts (32 hunks)
  • packages/orm/src/client/crud/operations/create.ts (2 hunks)
  • packages/orm/src/client/crud/operations/delete.ts (2 hunks)
  • packages/orm/src/client/crud/operations/update.ts (4 hunks)
  • packages/orm/src/client/crud/validator/index.ts (3 hunks)
  • packages/orm/src/client/crud/validator/utils.ts (2 hunks)
  • packages/orm/src/client/errors.ts (2 hunks)
  • packages/orm/src/client/executor/error-processor.ts (1 hunks)
  • packages/orm/src/client/executor/zenstack-query-executor.ts (3 hunks)
  • packages/orm/src/client/index.ts (1 hunks)
  • packages/orm/src/client/query-utils.ts (9 hunks)
  • packages/plugins/policy/src/expression-transformer.ts (7 hunks)
  • packages/plugins/policy/src/policy-handler.ts (11 hunks)
  • packages/plugins/policy/src/utils.ts (2 hunks)
  • packages/server/src/api/rest/index.ts (4 hunks)
  • packages/server/src/api/rpc/index.ts (3 hunks)
  • packages/server/test/api/rest.test.ts (0 hunks)
  • packages/testtools/src/vitest-ext.ts (4 hunks)
  • tests/e2e/orm/client-api/delegate.test.ts (1 hunks)
  • tests/e2e/orm/client-api/error-handling.test.ts (1 hunks)
  • tests/e2e/orm/client-api/find.test.ts (4 hunks)
  • tests/e2e/orm/client-api/mixin.test.ts (1 hunks)
  • tests/e2e/orm/client-api/update.test.ts (1 hunks)
  • tests/e2e/orm/plugin-infra/on-kysely-query.test.ts (1 hunks)
  • tests/e2e/orm/policy/crud/update.test.ts (2 hunks)
  • tests/e2e/orm/policy/migrated/auth.test.ts (1 hunks)
  • tests/e2e/orm/policy/migrated/deep-nested.test.ts (1 hunks)
  • tests/e2e/orm/policy/migrated/multi-field-unique.test.ts (3 hunks)
  • tests/e2e/orm/policy/migrated/todo-sample.test.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • tests/e2e/orm/policy/migrated/todo-sample.test.ts
  • packages/server/test/api/rest.test.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-21T16:04:56.292Z
Learnt from: ymc9
Repo: zenstackhq/zenstack-v3 PR: 319
File: packages/runtime/src/client/crud/dialects/base-dialect.ts:745-747
Timestamp: 2025-10-21T16:04:56.292Z
Learning: In packages/runtime/src/client/crud/dialects/base-dialect.ts, it's intentional that buildCursorFilter applies default ordering (via makeDefaultOrderBy fallback) while buildOrderBy does not. This ensures cursor-based pagination always has stable ordering for correctness, while regular queries remain unordered unless explicitly specified. This design is to be consistent with Prisma's pagination requirements.

Applied to files:

  • packages/orm/src/client/crud/dialects/base-dialect.ts
📚 Learning: 2025-10-21T16:09:31.218Z
Learnt from: ymc9
Repo: zenstackhq/zenstack-v3 PR: 319
File: packages/runtime/src/client/executor/zenstack-query-executor.ts:63-72
Timestamp: 2025-10-21T16:09:31.218Z
Learning: In ZenStack, TypeDefs can be inherited by models. When a TypeDef contains fields with `map` attributes, those mapped field names need to be processed by the QueryNameMapper since they become part of the inheriting model's schema. Therefore, when checking if a schema has mapped names (e.g., in `schemaHasMappedNames`), both `schema.models` and `schema.typeDefs` must be inspected for `@map` and `map` attributes.

Applied to files:

  • packages/orm/src/client/crud/dialects/sqlite.ts
  • packages/orm/src/client/executor/zenstack-query-executor.ts
  • packages/orm/src/client/query-utils.ts
🔇 Additional comments (46)
packages/language/res/stdlib.zmodel (1)

385-386: LGTM! Documentation terminology updated consistently.

The documentation changes align well with the PR's objective to adopt ORM-centric terminology. The additional clarification that ignored fields/models are still recognized by database schema migrations is a helpful detail for users.

Also applies to: 391-392

packages/orm/src/client/crud/validator/utils.ts (2)

16-16: LGTM! Import correctly updated to use error factory.

The import statement correctly brings in the createNotSupportedError factory function, aligning with the PR's error system refactoring.


454-456: Verify consistency with other "unsupported" error cases.

The use of createNotSupportedError is correct. However, line 314 throws a generic Error for unsupported unary operators, which is conceptually similar. Consider updating line 314 for consistency:

-            throw new Error(`Unsupported unary operator: ${expr.op}`);
+            throw createNotSupportedError(`Unsupported unary operator: ${expr.op}`);

Run the following script to check for other generic Error throws that might need similar updates:

tests/e2e/orm/plugin-infra/on-kysely-query.test.ts (1)

214-214: LGTM: Error assertion updated for new error structure.

The test now correctly checks the nested cause.message property, aligning with the new error wrapping approach where database/plugin errors are wrapped in ORMError instances.

packages/orm/src/client/executor/error-processor.ts (1)

1-12: Function looks good, but note the TODO for future consideration.

The implementation safely checks for the code property before accessing it. The TODO on line 4 indicates awareness that different database drivers may structure errors differently. Consider documenting which database drivers are currently supported or add driver-specific code extraction logic if needed in the future.

packages/testtools/src/vitest-ext.ts (1)

1-20: LGTM: Error checking refactored to use unified ORMError system.

The rename from expectError to expectErrorReason and the updated logic correctly validates ORMError instances with the appropriate ORMErrorReason.

packages/orm/src/client/executor/zenstack-query-executor.ts (2)

107-122: LGTM: Error handling correctly updated to use error factories.

The logic appropriately preserves existing ORMError instances and wraps other errors using createDBQueryError with helpful context (SQL and parameters). This maintains the error chain while providing debugging information.


367-369: LGTM: Internal error creation updated to use factory.

The change from new InternalError(...) to createInternalError(...) aligns with the new error factory pattern.

packages/orm/src/client/crud/dialects/postgresql.ts (1)

438-442: LGTM: Error creation updated to use factory.

The change from throwing QueryError to using createInternalError is appropriate for this internal error case and aligns with the new error factory pattern.

tests/e2e/orm/client-api/delegate.test.ts (1)

185-185: LGTM: Error assertion updated for new error structure.

The test now correctly checks the nested cause.message property with case-insensitive matching, which is robust for different database driver error formats.

tests/e2e/orm/client-api/mixin.test.ts (1)

78-78: LGTM: Error assertion updated for new error structure.

The test now correctly checks the nested cause.message property, maintaining the case-insensitive matching from the original regex pattern.

tests/e2e/orm/policy/migrated/auth.test.ts (1)

539-541: LGTM: Error assertion updated for new error structure.

The test now correctly checks the nested cause.message property with case-insensitive matching. The multi-line format improves readability.

tests/e2e/orm/client-api/update.test.ts (1)

1053-1053: LGTM! Error assertion updated to new error structure.

The test now correctly inspects the error's cause chain, checking that e.cause.message contains 'constraint'. This aligns with the broader error system refactoring where database errors are wrapped with cause information.

tests/e2e/orm/policy/migrated/deep-nested.test.ts (1)

485-485: LGTM! Consistent error assertion pattern.

The test correctly inspects the error cause chain for constraint violations, matching the pattern used across the test suite.

tests/e2e/orm/policy/crud/update.test.ts (1)

761-761: LGTM! Consistent error assertions.

Both test assertions correctly check the error cause chain for constraint violations, aligning with the new error structure.

Also applies to: 1127-1127

packages/orm/src/client/crud/dialects/sqlite.ts (1)

15-15: LGTM! Error factory usage is correct.

The migration from QueryError to createInternalError is properly implemented across all three error sites. The JSON parsing error correctly preserves the cause chain, and the other two errors have appropriate messages.

Also applies to: 124-124, 379-379, 382-382

packages/orm/src/client/client-impl.ts (1)

32-32: LGTM! Error factory functions properly implemented.

The migration from error classes to factory functions (createConfigError and createNotFoundError) is correct. Both error sites use the appropriate factory with proper arguments.

Also applies to: 226-226, 392-392

packages/orm/src/client/index.ts (1)

6-6: LGTM! Public API surface appropriately scoped.

The change from wildcard export to named exports limits the public API to only ORMError, ORMErrorReason, and RejectedByPolicyReason. This is appropriate as consumers only need these types for error handling, while factory functions remain internal implementation details.

packages/orm/src/client/crud/operations/update.ts (1)

4-4: LGTM! Consistent error factory usage.

The migration from RejectedByPolicyError class to createRejectedByPolicyError factory is correctly implemented across all three error sites (update, updateManyAndReturn, and upsert). Each call properly passes the model, reason (CANNOT_READ_BACK), and descriptive message.

Also applies to: 64-68, 123-127, 171-175

README.md (1)

38-84: LGTM! Documentation improvements.

The heading updates improve consistency with title case formatting, and the added manual setup instructions provide clearer guidance for users.

tests/e2e/orm/client-api/error-handling.test.ts (1)

1-49: LGTM! Comprehensive error handling test coverage.

The test suite properly validates the new ORMError-based error handling system across different error scenarios: validation errors, not found errors, policy rejection, and database constraint violations. The provider-specific error code handling (lines 42-43) is appropriate.

packages/orm/src/client/crud/operations/create.ts (2)

4-4: LGTM! Import updated to use error factory.

The import correctly switches from the RejectedByPolicyError class to the createRejectedByPolicyError factory function, aligning with the unified error system refactor.


51-55: LGTM! Error construction updated to use factory.

The error creation now uses the factory function instead of direct class instantiation, maintaining the same error semantics while aligning with the new error handling approach.

tests/e2e/orm/client-api/find.test.ts (3)

55-56: LGTM! Test assertion updated for new error system.

The assertion now uses the toBeRejectedByValidation() matcher, which is cleaner and aligns with the unified error handling approach.


391-392: LGTM! Test assertion updated for not-found errors.

The assertion correctly uses the toBeRejectedNotFound() matcher for the findUniqueOrThrow case.


405-406: LGTM! Test assertion updated consistently.

The findFirstOrThrow test correctly uses the same toBeRejectedNotFound() matcher, maintaining consistency across similar test cases.

packages/plugins/policy/src/utils.ts (2)

162-171: LGTM! Error factory for policy rejection.

The createRejectedByPolicyError factory function properly constructs an ORMError with the appropriate reason and metadata (model, rejectedByPolicyReason), providing a centralized way to create policy-related errors.


173-175: LGTM! Error factory for unsupported operations.

The createUnsupportedError factory provides a clean way to create NOT_SUPPORTED errors, aligning with the unified error handling approach.

packages/plugins/policy/src/expression-transformer.ts (6)

43-51: LGTM! Updated imports for error factory.

The import statement now includes createUnsupportedError from the utils module, enabling the use of the centralized error factory throughout this file.


95-96: LGTM! Appropriate use of invariant.

Switching from throwing an error to using invariant(false, ...) is appropriate here, as this represents a programming invariant violation rather than a user-facing error scenario.


301-303: LGTM! Error factory used for unsupported operators.

The error throw now uses createUnsupportedError, providing consistent error handling for unsupported operators in policy expressions.


321-323: LGTM! Consistent error factory usage.

The unsupported auth() comparison scenario now uses the error factory, maintaining consistency with other error paths.


390-391: LGTM! Factory used for unimplemented functions.

Function implementation errors now use the centralized factory, providing consistent error reporting.


447-447: LGTM! Consistent argument error handling.

Unsupported argument expressions now use the error factory, completing the consistent error handling refactor in this file.

packages/orm/src/client/crud/dialects/base-dialect.ts (9)

17-17: LGTM! Error factory imports added.

The import statement now includes the three error factory functions needed throughout this file, enabling the transition from direct error class instantiation to factory-based error creation.


98-99: LGTM! Factory used for unsupported distinct.

The error for unsupported distinct operations now uses createNotSupportedError, providing consistent error handling across the codebase.


485-486: LGTM! Validation error uses factory.

Invalid array filter keys now use createInvalidInputError, maintaining consistent validation error reporting.


513-514: LGTM! Appropriate use of not-supported error.

The JSON filter limitation is correctly reported using createNotSupportedError, clearly indicating this is a feature gap rather than invalid user input.


516-517: LGTM! Validation error for unsupported fields.

Attempting to filter on Unsupported field types correctly uses createInvalidInputError.


592-592: LGTM! Factory used for invalid filter keys.

The error factory provides consistent handling for invalid filter keys in the standard filter builder.


645-646: LGTM! String filter validation uses factory.

Invalid string filter keys now use the error factory, maintaining consistency with other validation errors.


818-819: LGTM! OrderBy validation error.

Invalid orderBy values for to-many relations correctly use createInvalidInputError.


1087-1088: LGTM! Configuration error factory used.

Missing computed field implementations now use createConfigError, which is the appropriate error type for configuration issues rather than user input or runtime problems.

packages/orm/src/client/crud/operations/delete.ts (3)

4-4: LGTM! Imports updated to error factories.

The import statement correctly switches from error classes to factory functions, aligning with the unified error handling refactor.


37-38: LGTM! Not-found error uses factory.

The error creation for when no record is deleted now uses the createNotFoundError factory function, maintaining the same error semantics.


43-47: LGTM! Policy rejection error uses factory.

The policy rejection error construction now uses the factory function with appropriate reason and message, consistent with the error handling refactor.

@claude
Copy link

claude bot commented Nov 7, 2025

Claude finished @ymc9's task —— View job


PR Review: Error System Refactoring

I've completed a comprehensive review of this error system refactoring. This is a well-structured improvement that standardizes error handling across ZenStack.

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Analyze core error handling changes in errors.ts
  • Review new error processor implementation
  • Examine error handling across ORM components
  • Check policy plugin error handling changes
  • Review server API error handling updates
  • Analyze test coverage and new error tests
  • Look for potential security/performance issues
  • Provide comprehensive feedback and recommendations

✅ Strengths

1. Well-structured Error Hierarchy

  • Clear separation between ORMErrorReason and RejectedByPolicyReason enums
  • Comprehensive error types covering all major failure scenarios
  • Good use of TypeScript error constructor with proper inheritance from Error

2. Comprehensive Error Context

  • Rich error metadata (model, dbErrorCode, sql, sqlParams, etc.)
  • Policy-specific error reasons for better debugging
  • Proper error chaining with cause parameter

3. Consistent API Integration

  • Both REST and RPC APIs properly map ORM errors to appropriate HTTP status codes
  • Good separation of concerns with dedicated error handling methods
  • Proper logging integration

4. Good Test Coverage

  • New test file covers major error scenarios (validation, not found, policy rejection, DB errors)
  • Tests verify both error types and specific error codes

🔍 Areas for Improvement

1. Error Processor Implementation (packages/orm/src/client/executor/error-processor.ts:6-12)

export function getDbErrorCode(error: unknown): unknown | undefined {
    if (error instanceof Error && 'code' in error) {
        return error.code;
    } else {
        return undefined;
    }
}
  • Issue: Very basic implementation with @todo comment
  • Recommendation: Consider adding type-specific error code extraction for different database drivers (SQLite vs PostgreSQL have different error formats)

2. Generic Error Type for dbErrorCode (packages/orm/src/client/errors.ts:83)

public dbErrorCode?: unknown;
  • Issue: Using unknown type loses type safety benefits
  • Recommendation: Consider a union type like string | number or create specific types for each database driver

3. Security Considerations

  • SQL Exposure: Error objects include sql and sqlParams fields which could expose sensitive data in logs
  • Recommendation: Consider sanitizing or limiting SQL exposure in production environments
  • Database Error Details: Raw database errors might leak internal structure information

4. Error Message Consistency (packages/orm/src/client/crud/operations/base.ts)

  • Some error messages are hardcoded strings while others are parameterized
  • Consider using a consistent error message templating system

🚀 Performance Considerations

1. Error Creation Performance

  • Factory functions like createDBQueryError are efficient
  • No unnecessary object creation or string manipulation

2. Error Processing Overhead

  • The error processor function is lightweight
  • Policy error checking integrates well without significant overhead

🔒 Security Assessment

1. Information Disclosure Risks

  • Medium Risk: SQL queries and parameters exposed in error objects
  • Low Risk: Database error codes might reveal schema information
  • Mitigation: Consider environment-specific error detail levels

2. Error Handling Robustness

  • Good error boundaries prevent unhandled exceptions
  • Proper error chaining maintains context without losing information

📋 Minor Issues

1. Type Safety (packages/orm/src/client/errors.ts:122-128)

export function createDBQueryError(message: string, dbError: unknown, sql: string, parameters: readonly unknown[]) {
  • Consider adding generic constraints for better type safety

2. Documentation

  • The @todo comment in error processor should be addressed or tracked in issues

3. Deleted Test File

  • The removal of tests/e2e/orm/policy/migrated/todo-sample.test.ts should be explained in the PR description

🎯 Recommendations

  1. Immediate: Address the @todo in error processor or create an issue to track it
  2. Short-term: Consider implementing environment-specific error detail levels for security
  3. Long-term: Evaluate adding structured error codes for better programmatic error handling

Overall Assessment

This is a solid refactoring that significantly improves error handling consistency across ZenStack. The new error system is well-designed, properly tested, and integrates cleanly with existing APIs. The main concerns are around information disclosure and the incomplete error processor implementation.

Recommendation: ✅ Approve with minor follow-ups

@ymc9 ymc9 merged commit ff0808d into dev Nov 7, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants