fix: prevent FIND_RECORDS from silently dropping unresolved filter variables (#18814)
## Summary Fixes #18744 — The workflow FIND_RECORDS action silently drops filter conditions when a variable resolves to null/empty, causing the query to return **all records** instead of erroring. **Root cause (three compounding layers):** 1. **`variable-resolver.ts`** — `resolveString` returns `undefined` when a variable lookup fails (e.g., `{{steps.trigger.output.userId}}` where `userId` doesn't exist in context). The return type says `string` but `evalFromContext` actually returns `undefined` at runtime. 2. **`checkIfShouldSkipFiltering.ts`** — Treats `undefined`/`null`/`""` values as "skip this filter." This is correct for the **UI filter builder** (user hasn't finished typing), but wrong for **workflow execution** (variable resolution failed = misconfigured workflow). 3. **`find-records.workflow-action.ts`** — When all filters are silently skipped, `computeRecordGqlOperationFilter` returns `{}` (match everything). The query runs with no filter, returning all records — silently succeeding with wrong results. ## Fix Added validation in `find-records.workflow-action.ts` **after** `resolveInput` but **before** `computeRecordGqlOperationFilter`. For each filter with a value-requiring operand (i.e., not IS_EMPTY, IS_NOT_EMPTY, IS_IN_PAST, IS_IN_FUTURE, IS_TODAY), if the resolved value is `undefined`, `null`, or `""`, throw `INVALID_STEP_INPUT` with a descriptive error message. **Why this approach:** - Scoped to the workflow executor — does **not** break the UI filter builder's intentional skip-on-empty behavior - Does not change shared utilities (`checkIfShouldSkipFiltering`, `resolveInput`) used across the app - Fails fast with a clear error instead of silently returning wrong data - 1 file changed, 23 lines added ## Test plan - [x] Backend typecheck passes - [x] oxlint passes (0 warnings, 0 errors) - [x] Prettier passes - [ ] Manual: Create a workflow with FIND_RECORDS using a variable that doesn't exist → should error with "Filter condition has an empty value after variable resolution" instead of returning all records - [ ] Manual: Create a workflow with FIND_RECORDS using IS_EMPTY operand (no value needed) → should still work correctly --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+12
@@ -7,6 +7,7 @@ import {
|
||||
import {
|
||||
computeRecordGqlOperationFilter,
|
||||
isDefined,
|
||||
isRecordFilterValueValid,
|
||||
resolveInput,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
@@ -94,6 +95,17 @@ export class FindRecordsWorkflowAction implements WorkflowAction {
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
if (workflowActionInput.filter?.recordFilters) {
|
||||
for (const filter of workflowActionInput.filter.recordFilters) {
|
||||
if (!isRecordFilterValueValid(filter)) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
`Filter condition has an empty value after variable resolution. This likely means a workflow variable could not be resolved. Filter field: ${filter.fieldMetadataId}, operand: ${filter.operand}`,
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gqlOperationFilter =
|
||||
workflowActionInput.filter?.recordFilters &&
|
||||
workflowActionInput.filter?.recordFilterGroups
|
||||
|
||||
Reference in New Issue
Block a user