Files
twenty/packages
Sonarly Claude Code 16c2404d31 Race condition between two cache reads in view field creation causes entity not found
https://sonarly.com/issue/4058?type=bug

Creating view fields fails with BUILDER_INTERNAL_SERVER_ERROR when a related entity (fieldMetadata, view, or viewFieldGroup) exists in the first workspace cache read but is absent from the second, separate cache read used during migration building.

Fix: ## Root Cause

A TOCTOU race condition between two separate workspace cache reads in `ViewFieldService.createMany`:

- **Read #1** (lines 79–89 of `view-field.service.ts`): fetches `flatFieldMetadataMaps`, `flatViewMaps`, and `flatViewFieldGroupMaps` to resolve raw IDs (e.g. `fieldMetadataId`) into universal identifiers for building `flatViewFieldsToCreate`.
- **Read #2** (inside `computeAllRelatedFlatEntityMaps` in `workspace-migration-validate-build-and-run-service.ts`): fetches all related entity maps used by the migration builder.

If a concurrent metadata operation (field deletion, cache invalidation) occurs between Read #1 and Read #2, the referenced entity (e.g. `fieldMetadata` resolved in Read #1) may be absent from the second snapshot. When the migration builder then calls `findFlatEntityByUniversalIdentifierOrThrow` on the Read #2 snapshot, it throws `FlatEntityMapsException → BUILDER_INTERNAL_SERVER_ERROR`.

## Fix

Pass the maps fetched in Read #1 as `preloadedFlatEntityMaps` to `validateBuildAndRunWorkspaceMigration`. Inside `computeAllRelatedFlatEntityMaps`, the pre-loaded maps are overlaid over the cache-fetched maps after Read #2:

```typescript file=packages/twenty-server/src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service.ts lines=210-219
const { flatApplicationMaps, ...cachedFlatEntityMaps } =
  await this.workspaceCacheService.getOrRecompute(workspaceId, [...]);

const allRelatedFlatEntityMaps = isDefined(preloadedFlatEntityMaps)
  ? { ...cachedFlatEntityMaps, ...preloadedFlatEntityMaps }
  : cachedFlatEntityMaps;
```

This guarantees the entities resolved during ID-to-universal-identifier translation are present in the maps used for migration building — even if the cache is invalidated between the two async operations. Keys not covered by `preloadedFlatEntityMaps` (e.g. `featureFlagsMap`, `flatObjectMetadataMaps`) still come from the cache normally.

The call site in `view-field.service.ts` now passes:

```typescript file=packages/twenty-server/src/engine/metadata-modules/view-field/services/view-field.service.ts lines=116-120
preloadedFlatEntityMaps: {
  flatFieldMetadataMaps,
  flatViewMaps,
  flatViewFieldGroupMaps,
},
```
2026-03-03 06:50:36 +00:00
..
2026-02-18 23:34:36 +01:00