Skip to content

Distinguish parse errors from execution errors with different exception types #23

@hardbyte

Description

@hardbyte

Problem

Currently, the library raises ValueError for both parse/syntax errors and execution errors (like type mismatches). This makes it difficult for users to distinguish between:

  1. Configuration errors (invalid CEL syntax) - These should be surfaced immediately to the user
  2. Runtime validation failures (type errors, etc.) - These are expected failures during normal operation

Current Behavior

from cel import cel

# Parse error - raises ValueError
try:
    cel.evaluate('invalid syntax +++', cel.Context())
except ValueError as e:
    print(f'Parse error: {e}')
    # Output: Failed to parse expression 'invalid syntax +++'...

# Type error - also raises ValueError
try:
    cel.evaluate('"string" > 100', cel.Context(variables={'value': 'string'}))
except ValueError as e:
    print(f'Type error: {e}')
    # Output: CEL execution error: String("string") can not be compared to Int(100)...

Desired Behavior

It would be helpful if the library used different exception types:

  • Parse/syntax errors: ValueError (or a specific CELParseError subclass)
  • Execution errors: RuntimeError (or a specific CELExecutionError subclass)
  • Undefined variables: RuntimeError (already works this way ✅)

Workaround

Currently working around this by checking the error message string:

try:
    result = cel.evaluate(validation_rule, env)
except ValueError as e:
    if "Failed to parse" in str(e):
        # Parse error - re-raise
        raise ValueError(f"Invalid CEL expression: {e}") from e
    else:
        # Execution error - treat as validation failure
        return False

Use Case

In netcheck, we use CEL for validation rules. When a user provides an invalid rule (syntax error), we want to fail immediately with a clear error. When execution fails (type mismatch, etc.), we want to treat it as a failed validation check.

Proposed Solution

Option 1: Use existing Python exceptions

raise ValueError("Parse error: ...")  # For syntax errors
raise RuntimeError("Execution error: ...")  # For type/execution errors

Option 2: Create custom exception classes

class CELParseError(ValueError): ...
class CELExecutionError(RuntimeError): ...

Either approach would allow consumers to handle these cases appropriately without relying on error message parsing.

Related

This is similar to how the Python celpy library handles errors:

  • CELParseError for parse errors
  • CELEvalError for evaluation errors

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions