Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 694b481ed0 fix: skip unknown fields in SSE optimistic record updates instead of throwing
https://sonarly.com/issue/18991?type=bug

When SSE events deliver record data containing custom fields (e.g. mail, pitched, media, number, businessCategory on Company), `computeOptimisticRecordFromInput` throws a fatal error if the frontend's cached object metadata doesn't include those fields, breaking the entire SSE event processing stream.

Fix: **What changed:** In `computeOptimisticRecordFromInput.ts`, replaced `throw new Error(...)` with `console.warn(...)` when unknown fields are detected in the record input (line 73-79).

**Why:** The function's main for-loop (line 82+) iterates only over `objectMetadataItem.fields`, so unknown fields in `recordInput` are already naturally skipped and never processed. The `throw` was a defensive assertion added in commit `29745c67568` that assumed the caller always provides frontend-controlled input. However, when SSE real-time events were added, this function started receiving server-controlled input via `useTriggerOptimisticEffectFromSseUpdateEvents.ts`, which passes `event.properties.after` directly — including custom fields the user added that may not yet be in the frontend's stale metadata cache.

Converting to `console.warn` preserves observability while allowing graceful degradation. The known fields are still processed correctly.

**Test update:** Updated the existing test case from asserting a throw to asserting a `console.warn` call and verifying that known fields (like `city: 'Paris'`) are still correctly included in the output while unknown fields are skipped.
2026-03-27 12:58:47 +00:00
2 changed files with 28 additions and 19 deletions
@@ -252,26 +252,32 @@ describe('computeOptimisticRecordFromInput', () => {
});
});
it('should throw an error if recordInput contains fields unrelated to the current objectMetadata', () => {
it('should warn and skip unknown fields if recordInput contains fields unrelated to the current objectMetadata', () => {
const cache = new InMemoryCache();
const personObjectMetadataItem = getMockObjectMetadataItemOrThrow('person');
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
expect(() =>
computeOptimisticRecordFromInput({
currentWorkspaceMember,
objectMetadataItems: getTestEnrichedObjectMetadataItemsMock(),
objectMetadataItem: personObjectMetadataItem,
recordInput: {
unknwon: 'unknown',
foo: 'foo',
bar: 'bar',
city: 'Paris',
},
cache,
objectPermissionsByObjectMetadataId: {},
}),
).toThrowErrorMatchingInlineSnapshot(
`"Should never occur, encountered unknown fields unknwon, foo, bar in objectMetadataItem person"`,
const result = computeOptimisticRecordFromInput({
currentWorkspaceMember,
objectMetadataItems: getTestEnrichedObjectMetadataItemsMock(),
objectMetadataItem: personObjectMetadataItem,
recordInput: {
unknwon: 'unknown',
foo: 'foo',
bar: 'bar',
city: 'Paris',
},
cache,
objectPermissionsByObjectMetadataId: {},
});
expect(warnSpy).toHaveBeenCalledWith(
'computeOptimisticRecordFromInput: skipping unknown fields unknwon, foo, bar in objectMetadataItem person',
);
expect(result).toEqual({
city: 'Paris',
});
warnSpy.mockRestore();
});
});
@@ -71,8 +71,11 @@ export const computeOptimisticRecordFromInput = ({
},
);
if (unknownRecordInputFields.length > 0) {
throw new Error(
`Should never occur, encountered unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}`,
// Unknown fields can appear when SSE events include custom fields
// not yet present in the frontend metadata cache. The for-loop below
// iterates only over known fields, so unknown ones are safely skipped.
console.warn(
`computeOptimisticRecordFromInput: skipping unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}`,
);
}