From afad6a39093009a1730140d6189af60de8e52ec0 Mon Sep 17 00:00:00 2001 From: Tushar Verma Date: Thu, 13 Aug 2026 21:57:28 +0530 Subject: [PATCH 1/2] libimage: wrap the isCorrupted error Signed-off-by: Tushar Verma --- common/libimage/image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/libimage/image.go b/common/libimage/image.go index ea102f81ca..2b6cf32394 100644 --- a/common/libimage/image.go +++ b/common/libimage/image.go @@ -102,7 +102,7 @@ func (i *Image) isCorrupted(ctx context.Context, name string) error { if name == "" { name = i.ID()[:12] } - return fmt.Errorf("Image %s exists in local storage but may be corrupted (remove the image to resolve the issue): %v", name, err) + return fmt.Errorf("Image %s exists in local storage but may be corrupted (remove the image to resolve the issue): %w", name, err) } return img.Close() } From 4544bf0ce3d4a4da27502874fc739ec3a01f0185 Mon Sep 17 00:00:00 2001 From: Tushar Verma Date: Thu, 13 Aug 2026 21:57:28 +0530 Subject: [PATCH 2/2] libimage: skip removed images in DiskUsage Signed-off-by: Tushar Verma --- common/libimage/corrupted_test.go | 26 ++++++++++++++++++++++++++ common/libimage/disk_usage.go | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/common/libimage/corrupted_test.go b/common/libimage/corrupted_test.go index 0275b550d4..a86436b43e 100644 --- a/common/libimage/corrupted_test.go +++ b/common/libimage/corrupted_test.go @@ -91,3 +91,29 @@ func TestCorruptedLayers(t *testing.T) { _, err = image.Inspect(ctx, nil) require.NoError(t, err, "inspecting repaired image should work") } + +func TestDiskUsageRemovedImage(t *testing.T) { + // An image can be removed by a parallel process between DiskUsage's + // listing and the per-image walk. The stale handle then fails with + // "image not known", which is not a corruption. DiskUsage skips it; + // that requires the wrapped error to keep its type. + runtime := testNewRuntime(t) + ctx := context.Background() + pullOptions := &PullOptions{} + pullOptions.Writer = os.Stdout + + pulledImages, err := runtime.Pull(ctx, "quay.io/libpod/alpine_nginx:latest", config.PullPolicyAlways, pullOptions) + require.NoError(t, err) + require.Len(t, pulledImages, 1) + staleImage := pulledImages[0] + + _, errs := runtime.store.DeleteImage(staleImage.ID(), true) + require.Nil(t, errs, "deleting the image from the store") + + err = staleImage.isCorrupted(ctx, "") + require.Error(t, err, "stale handle reports an error") + require.ErrorIs(t, err, storage.ErrImageUnknown, "the wrapped error keeps its type") + + _, _, err = runtime.DiskUsage(ctx) + require.NoError(t, err, "disk usage does not fail because of a removed image") +} diff --git a/common/libimage/disk_usage.go b/common/libimage/disk_usage.go index ccca5c9b0e..4523bd3bd5 100644 --- a/common/libimage/disk_usage.go +++ b/common/libimage/disk_usage.go @@ -4,6 +4,7 @@ package libimage import ( "context" + "errors" "time" "github.com/sirupsen/logrus" @@ -73,6 +74,12 @@ func (r *Runtime) DiskUsage(ctx context.Context) ([]ImageDiskUsage, int64, error for _, image := range images { usages, err := diskUsageForImage(ctx, image, layerMap, layerCount, &totalSize) if err != nil { + // The image can be removed by a parallel process between the + // listing above and reading it here. That is not a corruption, + // skip it instead of failing the whole disk usage. + if errors.Is(err, storage.ErrImageUnknown) { + continue + } return nil, -1, err } allUsages = append(allUsages, usages...)