Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 869d929b13 fix: gracefully handle missing field in isRecordMatchingFilter during optimistic update
https://sonarly.com/issue/18157?type=bug

Creating a new record on a custom object crashes when the Apollo cache contains queries with filters referencing join column fields (like `phoneNumberId`) that don't exist on that custom object's metadata.

Fix: Replaced the hard `throw` in `isRecordMatchingFilter` (line 218) with `return true` when a filter key cannot be resolved to any field in the object's metadata.

**What changed:** When `isRecordMatchingFilter` encounters a filter key that doesn't match any field in the object metadata (via direct name lookup, relation joinColumnName, or morph relation computed join columns), it now returns `true` instead of throwing an error.

**Why `return true`:** This treats an unresolvable filter condition as vacuously satisfied. This is safe because:
1. The server already returns correctly filtered data — this function only determines optimistic cache membership
2. Returning `true` preserves records in cached queries, avoiding incorrect eviction
3. A brief UI inconsistency until the server response arrives is strictly better than a crash

**Why this was crashing:** During optimistic cache updates (`triggerCreateRecordsOptimisticEffect`), Apollo iterates over ALL cached queries for an object type, calling `isRecordMatchingFilter` with each query's stored filter variables. When a cached query filter references a join column field (like `phoneNumberId`) that doesn't exist on the target custom object's metadata, the field lookup fails and the throw crashes the entire record creation flow.

Added a test case verifying that filters referencing non-existent fields return `true` instead of throwing.
2026-03-25 01:00:09 +00:00
2 changed files with 20 additions and 6 deletions
@@ -535,4 +535,20 @@ describe('isRecordMatchingFilter', () => {
).toBe(false);
});
});
describe('Missing field metadata', () => {
it('should return true when filter references a field not in object metadata', () => {
const filter: RecordGqlOperationFilter = {
nonExistentFieldId: { eq: 'some-value' },
};
expect(
isRecordMatchingFilter({
record: companiesMock[0],
filter,
objectMetadataItem: companyMockObjectMetadataItem,
}),
).toBe(true);
});
});
});
@@ -215,12 +215,10 @@ export const isRecordMatchingFilter = ({
);
if (!isDefined(objectMetadataField)) {
throw new Error(
'Field metadata item "' +
filterKey +
'" not found for object metadata item ' +
objectMetadataItem.nameSingular,
);
// Stale cached queries can reference fields that no longer exist
// in the metadata (e.g. deleted or deactivated custom fields).
// Skip the filter condition to avoid crashing optimistic updates.
return true;
}
switch (objectMetadataField.type) {