Skip to content

fix: handle missing metadata keys in CEL resource filter#960

Merged
adityachoudhari26 merged 1 commit intomainfrom
missing-key-doesnt-crash-cel
Apr 9, 2026
Merged

fix: handle missing metadata keys in CEL resource filter#960
adityachoudhari26 merged 1 commit intomainfrom
missing-key-doesnt-crash-cel

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Apr 9, 2026

Resolves #958

Summary by CodeRabbit

  • Bug Fixes

    • Improved resilience in resource listing: CEL filter evaluation errors no longer cause request failures. Resources with invalid filter evaluations are now gracefully excluded from results.
  • Tests

    • Enhanced test coverage for CEL filtering edge cases, including special characters and metadata scenarios.

Copilot AI review requested due to automatic review settings April 9, 2026 21:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Added try/catch error handling around CEL evaluation in resource listing to gracefully exclude resources on evaluation failure instead of propagating exceptions. Updated related e2e tests to better cover CEL metadata filtering scenarios with special characters and key presence validation.

Changes

Cohort / File(s) Summary
CEL Error Handling & Tests
apps/api/src/routes/v1/workspaces/resources.ts, e2e/tests/api/resources.spec.ts
API: wrapped cel-js evaluate in try/catch to exclude resources on error. Tests: refactored CEL filter scenarios to test metadata filtering with special characters (tag, a&b keys) and validate key presence/absence behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 With try-catch guards so clever and kind,
Resources falter, no crash left behind!
Metadata tags dance with ampersands bright,
Test cases whisper what's wrong and what's right. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: handle missing metadata keys in CEL resource filter' directly addresses the main change: wrapping CEL evaluation in try/catch to handle missing metadata keys gracefully instead of crashing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch missing-key-doesnt-crash-cel

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
Copy Markdown

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

Updates resource listing CEL filtering to avoid request failures when selectors reference missing metadata keys, and adjusts E2E coverage to exercise metadata-based selectors and missing-key behavior.

Changes:

  • Treat CEL evaluation errors during resource filtering as non-matches (skip the resource instead of failing the request).
  • Update E2E tests to filter on resource.metadata[...] (including +, &, =) and add a missing-metadata-key scenario.

Reviewed changes

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

File Description
e2e/tests/api/resources.spec.ts Shifts several CEL filter tests to metadata-based selectors and adds coverage for missing metadata keys.
apps/api/src/routes/v1/workspaces/resources.ts Wraps cel-js evaluate() in a try/catch so evaluation failures don’t crash the list endpoint.

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

Comment on lines +52 to +56
try {
return evaluate(cel, { resource });
} catch {
return false;
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The catch-all around evaluate() will silently treat any runtime evaluation error as a non-match. That prevents 500s for missing metadata keys, but it also masks unexpected server-side bugs or selectors that parse successfully but are not evaluable (e.g., unsupported functions/fields), returning a misleading 200/empty result. Consider narrowing this to the specific evaluation error(s) you expect for missing keys (or checking error type/message), and rethrow/500 (or 400) for other failures; at minimum, capture the error and add bounded logging/telemetry so issues aren’t silently hidden.

Copilot uses AI. Check for mistakes.
Comment on lines 375 to 418
@@ -401,20 +414,31 @@ test.describe("Resource API", () => {
const listRes = await api.GET("/v1/workspaces/{workspaceId}/resources", {
params: {
path: { workspaceId: workspace.id },
query: { cel: `resource.kind == "${kind}"` },
query: { cel: 'resource.metadata["env"] == "production"' },
},
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

This test previously covered CEL filters containing a literal "%20" in the selector value, but it has been replaced with the missing-metadata-key scenario. There are no remaining "%20"-related assertions in this file, so the URL-decoding regression the prior test guarded against is no longer covered. If handling literal "%20" is still required, keep that case as a separate test (or add a new one) rather than overwriting it here.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
e2e/tests/api/resources.spec.ts (1)

375-445: Use YAML fixtures for this new e2e case.

This adds ad-hoc resource setup/teardown inside the test body instead of declaring the entities in the adjacent .spec.yaml and using the fixture helpers. Moving it there would keep the new regression aligned with the repo’s e2e conventions.

As per coding guidelines, "E2E tests use YAML fixture files (.spec.yaml alongside .spec.ts) to declare test entities; use importEntitiesFromYaml to load them and cleanupImportedEntities to tear them down".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/api/resources.spec.ts` around lines 375 - 445, The test "should
skip resources where CEL references a missing metadata key" currently creates
and deletes resources inline; instead add those resources to the adjacent
.spec.yaml fixture and load/teardown them using the e2e helpers: declare both
resources in the .spec.yaml fixture file (matching identifiers used in the
test), then in the .spec.ts replace the inline PUT/DELETE calls with
importEntitiesFromYaml(...) at test setup (or beforeEach) and call
cleanupImportedEntities(...) in teardown (or afterEach); remove the ad-hoc API
calls and keep the list/expect assertions using the fixture identifiers so the
test follows the repository's e2e fixture conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/routes/v1/workspaces/resources.ts`:
- Around line 52-56: The catch in validResourceSelector currently swallows all
exceptions from evaluate(cel, { resource }) causing runtime missing-field errors
(e.g., resource.metadata.nonexistent) to be treated as false; change the handler
to only catch/handle missing-key/runtime-lookup errors (or rethrow unexpected
exceptions) and/or log them: specifically, in the function that calls
evaluate(cel, { resource }) (validResourceSelector), detect the error
type/message for missing metadata keys and return false for that case, but for
any other exception rethrow or log and propagate an error response so the
endpoint does not silently succeed.

---

Nitpick comments:
In `@e2e/tests/api/resources.spec.ts`:
- Around line 375-445: The test "should skip resources where CEL references a
missing metadata key" currently creates and deletes resources inline; instead
add those resources to the adjacent .spec.yaml fixture and load/teardown them
using the e2e helpers: declare both resources in the .spec.yaml fixture file
(matching identifiers used in the test), then in the .spec.ts replace the inline
PUT/DELETE calls with importEntitiesFromYaml(...) at test setup (or beforeEach)
and call cleanupImportedEntities(...) in teardown (or afterEach); remove the
ad-hoc API calls and keep the list/expect assertions using the fixture
identifiers so the test follows the repository's e2e fixture conventions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c0c444d-b1e2-44c7-9b78-d85e3f9b9bc6

📥 Commits

Reviewing files that changed from the base of the PR and between a6f9c3b and a9ab419.

📒 Files selected for processing (2)
  • apps/api/src/routes/v1/workspaces/resources.ts
  • e2e/tests/api/resources.spec.ts

Comment on lines +52 to +56
try {
return evaluate(cel, { resource });
} catch {
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect selector validation and the new catch block side by side.
sed -n '1,220p' apps/api/src/routes/v1/valid-selector.ts
printf '\n--- resources.ts ---\n'
sed -n '39,60p' apps/api/src/routes/v1/workspaces/resources.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 955


🏁 Script executed:

# Find the evaluate function definition and its documentation
rg -B 3 -A 10 "export.*evaluate|function evaluate" apps/api/src --type ts --type tsx

# Check for test cases around resource filtering and CEL evaluation
fd -e test.ts -e spec.ts apps/api/src | xargs rg -l "evaluate\|resources" | head -5

# Look for any comments or context around the try/catch addition
git log --oneline -S "catch {" apps/api/src/routes/v1/workspaces/resources.ts | head -3

Repository: ctrlplanedev/ctrlplane

Length of output: 530


🏁 Script executed:

# Find evaluate function definition
fd . apps/api --full-path | xargs rg -l "export.*evaluate|function evaluate" | head -3

# Search for evaluate in the codebase more broadly
rg "evaluate\(" apps/api/src --type ts -B 2 -A 8 | head -50

# Check if there's any specific error handling for missing keys elsewhere
rg "missing.*key|key.*missing|undefined.*field" apps/api/src --type ts

Repository: ctrlplanedev/ctrlplane

Length of output: 836


🏁 Script executed:

# Find where evaluate is imported from in resources.ts
head -40 apps/api/src/routes/v1/workspaces/resources.ts | grep -E "import|from"

# Find all files that export evaluate
rg "export.*evaluate" apps/ --type ts

# Find the evaluate function implementation
rg -B 5 -A 15 "const evaluate|function evaluate" apps/ --type ts

Repository: ctrlplanedev/ctrlplane

Length of output: 533


🏁 Script executed:

# Look for tests involving evaluate or CEL resource filtering
fd . apps/api --full-path -name "*.test.ts" -o -name "*.spec.ts" | xargs rg "evaluate|CEL|resource.*filter" 2>/dev/null | head -20

# Check if evaluate is used elsewhere in the codebase with different error handling
rg "evaluate\(" apps/ --type ts -B 3 -A 3

# Check for any comments or documentation about CEL evaluation errors
rg "cel-js|evaluate" apps/api/src --type ts -B 2 -A 2 | grep -i "error\|exception\|missing\|undefined"

# Look at the full resources.ts file around the filter to understand context
sed -n '30,75p' apps/api/src/routes/v1/workspaces/resources.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 5747


🏁 Script executed:

# Find cel-js package info and version
rg "cel-js" apps/ packages/ --type json

# Look for any tests involving CEL evaluation errors
fd . apps/api --full-path -type f | xargs grep -l "evaluate" | xargs grep -l "test\|spec" | head -3

# Check workspace-engine for similar patterns (from learnings, there's CEL usage there)
rg "evaluate\|cel" apps/workspace-engine/pkg/workspace/store/relationship_indexes.go -B 2 -A 5

# Look for error types or error messages related to missing keys
rg "missing\|undefined\|field\|key" apps/api/src/routes/v1/workspaces/resources.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 502


🏁 Script executed:

# Search for tests of the resources endpoint
fd . apps/api --full-path -type f \( -name "*.test.ts" -o -name "*.spec.ts" \) | xargs rg -l "resources" 2>/dev/null | head -5

# Look for any logging or error reporting around evaluate
rg "console\|log\|error" apps/api/src/routes/v1/workspaces/resources.ts

# Check if there are any comments or documentation in resources.ts about error handling
head -100 apps/api/src/routes/v1/workspaces/resources.ts | grep -E "//|/\*"

# Search entire resources.ts file for context
cat apps/api/src/routes/v1/workspaces/resources.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 13718


Broaden scope or narrow error handling in CEL evaluation catch block.

The commit targets "missing metadata keys," but the current implementation catches all exceptions from evaluate(). Since validResourceSelector only validates CEL syntax (not runtime evaluation), expressions like resource.metadata.nonexistent == "value" will parse fine, fail at runtime when the field is missing, and silently return false instead of surfacing an error. The endpoint then returns 200 with partial or empty results instead of an error response. Either explicitly handle only missing-key errors, log unexpected failures, or update the PR scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/routes/v1/workspaces/resources.ts` around lines 52 - 56, The
catch in validResourceSelector currently swallows all exceptions from
evaluate(cel, { resource }) causing runtime missing-field errors (e.g.,
resource.metadata.nonexistent) to be treated as false; change the handler to
only catch/handle missing-key/runtime-lookup errors (or rethrow unexpected
exceptions) and/or log them: specifically, in the function that calls
evaluate(cel, { resource }) (validResourceSelector), detect the error
type/message for missing metadata keys and return false for that case, but for
any other exception rethrow or log and propagate an error response so the
endpoint does not silently succeed.

@adityachoudhari26 adityachoudhari26 merged commit 731605a into main Apr 9, 2026
15 checks passed
@adityachoudhari26 adityachoudhari26 deleted the missing-key-doesnt-crash-cel branch April 9, 2026 21:21
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.

bug: list resource will fail if querying for metadata attribute that doesn't exist on resource

2 participants