Relationships
#2762 DuplicateTypeError names the incoming extension as the existing claimant and reports a false ghost row when it sorts first
Opened by hammz · 9/30/2026· Shipped 9/30/2026
Summary
ExtensionRepository.assertIRepo1 (src/infrastructure/persistence/extension_repository.ts ~687) scans catalog.findAll() and throws DuplicateTypeError with firstSource set to whichever conflicting row came first in the scan and secondSource to the later one. The order is the catalog's row order, not which extension already held the type. InstallExtensionService then treats firstSource as the pre-existing occupant: [REDACTED-SECRET-1] says the type is already claimed by firstSource and that secondSource cannot be installed, and isGhostRow stats firstSource.canonicalPath to detect a ghost row.
When the incoming extension's row sorts before the existing one, both are wrong:
- The message says the type is already claimed by the extension being installed, and that the already-installed extension cannot be installed.
- isGhostRow stats the incoming extension's file, which the collision rollback just deleted, so the message adds 'Ghost catalog entry detected (source deleted outside swamp)' although nothing was deleted outside swamp.
Reproduction
Found while reproducing swamp-club#2724, with the real installExtension path. Install @repro/b (claims model type @repro/shared), install @repro/a v1 (claims a distinct type), then upgrade @repro/a to v2, which also claims @repro/shared. Observed:
DuplicateTypeUserError: Type @repro/shared (kind=model) is already claimed by @repro/a@2026.01.02.1 at .../@repro/a/models/a_v2_model.ts. Cannot install @repro/b@2026.01.01.1 at .../@repro/b/models/b_model.ts — filesystem changes rolled back. Ghost catalog entry detected (source deleted outside swamp). Run swamp doctor extensions to reclassify and retry.Expected: already claimed by @repro/b; cannot install @repro/a v2; no ghost-row note.
Fix direction
Identify the incoming side by the extensions being saved (e.g. assertIRepo1 or saveAll knows which aggregates are in the current save), rather than by scan order, and make isGhostRow check the pre-existing side. Unit test with an incoming extension name that sorts before the existing one.
This is independent of swamp-club#2724's rollback fix; that fix changes which files exist after rollback but not the ordering.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.