Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneeshammz

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.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 9 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/30/2026, 1:34:59 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/30/2026, 1:01:43 PM

Sign in to post a ripple.