Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code ac3c1d817c fix: gracefully handle unknown fields in SSE optimistic record updates
https://sonarly.com/issue/17644?type=bug

SSE subscription events include all database columns (including custom fields), but `computeOptimisticRecordFromInput` throws a fatal error when it encounters fields not present in the client's object metadata, breaking real-time updates for workspaces with custom fields.

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

**Why:** The function validates that all fields in `recordInput` exist in `objectMetadataItem.fields`. This validation was originally written for mutation-triggered optimistic updates where the frontend controls the input shape. When SSE real-time events were added (via `useTriggerOptimisticEffectFromSseUpdateEvents`), this function started receiving server-controlled input that includes ALL database columns — including custom fields the workspace has added (e.g., Salesforce-migrated fields like `salesforceAccountId`, `fiscalYear`, `forecastCategory`).

The function's main for-loop (line 80) iterates only over `objectMetadataItem.fields`, so unknown fields in `recordInput` are already naturally skipped and never make it into the output. The throw was a defensive assertion that became incorrect when the caller contract expanded. Converting to `console.warn` preserves observability while allowing graceful degradation.

**Test update:** Updated the existing test from asserting a throw to asserting a `console.warn` call and verifying that known fields (like `city: 'Paris'`) are still correctly processed while unknown fields are skipped.
2026-03-23 18:14:30 +00:00
2 changed files with 25 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 consoleSpy = 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(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('encountered unknown fields unknwon, foo, bar'),
);
expect(result).toEqual({
city: 'Paris',
});
consoleSpy.mockRestore();
});
});
@@ -71,8 +71,8 @@ export const computeOptimisticRecordFromInput = ({
},
);
if (unknownRecordInputFields.length > 0) {
throw new Error(
`Should never occur, encountered unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}`,
console.warn(
`computeOptimisticRecordFromInput: encountered unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}. Skipping unknown fields.`,
);
}