Skip to content

Save blueprints via SaveBlueprint on the bulk save path (v16 backport) - #1036

Merged
KevinJump merged 1 commit into
KevinJump:v16/devfrom
Questo:fix/blueprint-bulk-save-v16
Aug 14, 2026
Merged

Save blueprints via SaveBlueprint on the bulk save path (v16 backport)#1036
KevinJump merged 1 commit into
KevinJump:v16/devfrom
Questo:fix/blueprint-bulk-save-v16

Conversation

@Questo

@Questo Questo commented Aug 14, 2026

Copy link
Copy Markdown

Backport of #1035 to v16/dev. Fixes #1034 on this branch.

ContentTemplateSerializer overrides SaveItemAsync to save through IContentService.SaveBlueprint, but inherits SaveAsync(IEnumerable<IContent>) from ContentSerializer, which saves through IContentService.Save.

SyncHandlerRoot.PerformSecondPassImportsAsync uses that bulk overload for any item whose second pass requires a save, so blueprints get persisted via DocumentRepository. Its PersistUpdatedItem rebuilds the NodeDto with DocumentRepository.NodeObjectTypeId, rewriting umbracoNode.nodeObjectType from DocumentBlueprint to Document.

Once that happens GetBlueprintById can no longer find the item, so the next import treats it as missing and tries to insert it again — failing with a duplicate key on IX_umbracoNode_UniqueId.

Same change as #1035, adjusted for this branch: wrapped in uSyncTaskHelper.FromResultOf and using the single argument SaveBlueprint(item), to match the surrounding code.

Verification

This is the branch the problem was originally found on. Against Umbraco 16.5.1 / uSync 16.1.0, on a site with 9 blueprints:

before after
blueprints with correct object type after first-boot import 3 / 9 9 / 9
duplicate key errors on the next import 6 0

Verified end to end by running a clean install twice — empty database and freshly seeded uSync folder, first boot then a second boot — and checking umbracoNode.nodeObjectType plus the import logs each time.

Take whichever of this and #1035 suits you, or neither if you would rather port it yourself.

ContentTemplateSerializer overrides SaveItemAsync to save through
IContentService.SaveBlueprint, but inherits SaveAsync(IEnumerable<IContent>)
from ContentSerializer, which saves through IContentService.Save.

That persists the blueprint through the regular document repository, and
DocumentRepository.PersistUpdatedItem rebuilds the NodeDto with its own
NodeObjectTypeId, rewriting umbracoNode.nodeObjectType from DocumentBlueprint
to Document. The blueprint is then invisible to GetBlueprintById, so the next
import treats it as missing and inserts it again, failing with a duplicate key
on IX_umbracoNode_UniqueId.

SyncHandlerRoot.PerformSecondPassImportsAsync calls SaveAsync for any item
whose second pass requires a save, so this is hit on first import of a
blueprint whose second pass makes changes.
@KevinJump
KevinJump merged commit 9288596 into KevinJump:v16/dev Aug 14, 2026
@KevinJump

Copy link
Copy Markdown
Owner

Not sure when this one will be released, v16 is technically end of life now, but we won't rule out fixes being bundled together as some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants