Skip to content

fix(copy): re-push referenced manifests when the destination reports them missing - #1213

Merged
TerryHowe merged 2 commits into
oras-project:v2from
Orexii:fix/copy-selfheal-missing-referenced-manifests-v2
Jul 15, 2026
Merged

TerryHowe merged 2 commits into
oras-project:v2from
Orexii:fix/copy-selfheal-missing-referenced-manifests-v2

Conversation

@Orexii

@Orexii Orexii commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Backport of #1212 to the v2 branch.

What this does

Makes CopyGraph self-heal a partially-copied graph. When the destination rejects a manifest push because it is missing referenced content (MANIFEST_BLOB_UNKNOWN / BLOB_UNKNOWN / MANIFEST_UNKNOWN), the copy now re-pushes the node's manifest successors and retries the push once, instead of returning the error.

Why

CopyGraph skips a node whose sub-DAG it believes already exists ("skip if a rooted sub-DAG exists"). If a prior copy was interrupted after a child manifest's blobs were uploaded but before the child manifest was committed, the destination can report the child as present (Exists/HEAD) yet reject the parent that references it. For a multi-arch image index this means the index push fails with MANIFEST_BLOB_UNKNOWN, and since the child is skipped on every run, re-running the same oras cp --recursive never recovers. This is the line that reaches the ORAS CLI.

This was hit in production mirroring a multi-arch image (mongo:7.0.34) between two registries; the only manual workaround was copying each child manifest by digest and re-pushing the index.

How

  • copyGraph: wrap the node push; on a "missing referenced content" error, re-copy the manifest successors unconditionally (bypassing the existence short-circuit) and retry the push once. No-op on the success path; blob successors are not re-pushed.
  • isMissingReferencedContentError: typed classification via errcode (handles both a single errcode.Error and a multi-error errcode.Errors); no string matching.

Testing

  • TestCopyGraph_SelfHealsInterruptedIndexCopy: end-to-end reproduction against a destination that emulates the interrupted state โ€” fails without this change (MANIFEST_BLOB_UNKNOWN), passes with it.
  • TestIsMissingReferencedContentError: unit test for the classifier (100% covered).
  • go test -race ./... passes; gofmt / go vet clean; new files carry the Apache license header.

The diff is identical to #1212 apart from the module import paths (github.com/oras-project/oras-go/v3 -> oras.land/oras-go/v2).

Relates to #1211.

โ€ฆthem missing

copyGraph skips a node when dst.Exists reports it present ("skip if a rooted
sub-DAG exists"). If a previous copy was interrupted after a referenced
manifest's blobs were uploaded but before that manifest itself was committed,
the destination can report the child manifest as existing while still rejecting
any manifest that references it. The push of the parent (e.g. an image index)
then fails with MANIFEST_BLOB_UNKNOWN, and because the child is skipped on every
subsequent run the recursive copy never self-heals.

When a node push fails because the destination is missing referenced content
(MANIFEST_BLOB_UNKNOWN / BLOB_UNKNOWN / MANIFEST_UNKNOWN), re-copy the node's
manifest successors unconditionally and retry the push once. This lets an
interrupted copy recover instead of failing identically on every retry. The
retry is a no-op on the success path.

Adds TestCopyGraph_SelfHealsInterruptedIndexCopy (end-to-end reproduction) and
TestIsMissingReferencedContentError (classifier unit test).

Signed-off-by: Malcolm Morgan <Malcolm.Morgan@Microsoft.com>
@TerryHowe TerryHowe added the v2 Things belongs to version 2.x label Jul 7, 2026
@Orexii

Orexii commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@TerryHowe anything needed to do on my end for this? Please let me know. Thanks

@TerryHowe TerryHowe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@TerryHowe
TerryHowe merged commit 60bfd16 into oras-project:v2 Jul 15, 2026
1 check passed
@TerryHowe TerryHowe mentioned this pull request Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Things belongs to version 2.x

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants