From 49a5c75e34b7c22b1ee5da10f7759d62b957d4d9 Mon Sep 17 00:00:00 2001 From: Devesh B <98201065+DeveshB-1@users.noreply.github.com> Date: Sun, 29 Mar 2026 23:50:18 +0530 Subject: [PATCH 1/2] fix(image): surface ENOSPC immediately on image load When disk space runs out during "podman image load", the error was buried in the image format detection loop and the user saw a misleading "payload does not match any of the supported image formats" error instead of the real cause. Detect "no space left on device" in each transport error and return syscall.ENOSPC immediately so callers and users see the real cause. Fixes: #26197 Signed-off-by: Devesh B <98201065+DeveshB-1@users.noreply.github.com> --- vendor/go.podman.io/common/libimage/load.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vendor/go.podman.io/common/libimage/load.go b/vendor/go.podman.io/common/libimage/load.go index 598bf39cdc1..296ba22816a 100644 --- a/vendor/go.podman.io/common/libimage/load.go +++ b/vendor/go.podman.io/common/libimage/load.go @@ -6,6 +6,8 @@ import ( "context" "errors" "fmt" + "strings" + "syscall" "time" "github.com/sirupsen/logrus" @@ -110,6 +112,11 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( return loadedImages, err } logrus.Debugf("Error loading %s (%s): %v", path, transportName, err) + // Surface ENOSPC immediately — no point trying other formats. + // The error arrives as a string from a subprocess, so check the message. + if strings.Contains(err.Error(), "no space left on device") { + return nil, fmt.Errorf("loading image: %w", syscall.ENOSPC) + } loadErrors = append(loadErrors, fmt.Errorf("%s: %v", transportName, err)) } From 98bcc07c88201c514dc2dd90072de09ff5dfbeae Mon Sep 17 00:00:00 2001 From: Devesh B <98201065+DeveshB-1@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:16:10 +0530 Subject: [PATCH 2/2] fix(image): move ENOSPC detection to abi layer; add unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit modified vendor/go.podman.io/common/libimage/load.go directly, which causes `make vendor` to report a dirty tree and fail the postbuild CI check. Move the "no space left on device" → syscall.ENOSPC wrapping into the non-vendored (*ImageEngine).Load wrapper in pkg/domain/infra/abi/images.go, and restore the vendor file to its canonical upstream state. Add a unit test (TestLoadImageENOSPCWrapping) that confirms the inline check maps the ENOSPC string to syscall.ENOSPC and leaves other errors unchanged. Signed-off-by: Devesh B <98201065+DeveshB-1@users.noreply.github.com> --- pkg/domain/infra/abi/images.go | 5 +++ pkg/domain/infra/abi/images_test.go | 35 +++++++++++++++++++++ vendor/go.podman.io/common/libimage/load.go | 7 ----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 55c84458b13..a2c1cca4d19 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -483,6 +483,11 @@ func (ir *ImageEngine) Load(ctx context.Context, options entities.ImageLoadOptio loadedImages, err := ir.Libpod.LibimageRuntime().Load(ctx, options.Input, loadOptions) if err != nil { + // Surface ENOSPC immediately — no point wrapping in a generic error. + // The error string arrives from a subprocess, so we check the message. + if strings.Contains(err.Error(), "no space left on device") { + return nil, fmt.Errorf("loading image: %w", syscall.ENOSPC) + } return nil, err } return &entities.ImageLoadReport{Names: loadedImages}, nil diff --git a/pkg/domain/infra/abi/images_test.go b/pkg/domain/infra/abi/images_test.go index 5739d604d09..cabac4df0b4 100644 --- a/pkg/domain/infra/abi/images_test.go +++ b/pkg/domain/infra/abi/images_test.go @@ -3,6 +3,10 @@ package abi import ( + "errors" + "fmt" + "strings" + "syscall" "testing" "github.com/stretchr/testify/assert" @@ -18,3 +22,34 @@ func TestToDomainHistoryLayer(t *testing.T) { newLayer := toDomainHistoryLayer(&layer) assert.Equal(t, layer.Size, newLayer.Size) } + +// TestLoadImageENOSPCWrapping verifies that the ENOSPC-detection logic used +// in (*ImageEngine).Load wraps disk-full errors so that callers can detect +// them with errors.Is(err, syscall.ENOSPC). +func TestLoadImageENOSPCWrapping(t *testing.T) { + // applyENOSPCCheck reproduces the inline check added to the Load function. + applyENOSPCCheck := func(loadErr error) error { + if loadErr == nil { + return nil + } + if strings.Contains(loadErr.Error(), "no space left on device") { + return fmt.Errorf("loading image: %w", syscall.ENOSPC) + } + return loadErr + } + + // A subprocess error that contains the canonical ENOSPC string. + enospcErr := errors.New("writing blob: no space left on device") + result := applyENOSPCCheck(enospcErr) + assert.True(t, errors.Is(result, syscall.ENOSPC), + "expected syscall.ENOSPC in error chain, got: %v", result) + + // A generic error must not be mistaken for ENOSPC. + otherErr := errors.New("permission denied") + result = applyENOSPCCheck(otherErr) + assert.False(t, errors.Is(result, syscall.ENOSPC), + "non-ENOSPC error must not wrap syscall.ENOSPC") + + // No error must pass through as nil. + assert.NoError(t, applyENOSPCCheck(nil)) +} diff --git a/vendor/go.podman.io/common/libimage/load.go b/vendor/go.podman.io/common/libimage/load.go index 296ba22816a..598bf39cdc1 100644 --- a/vendor/go.podman.io/common/libimage/load.go +++ b/vendor/go.podman.io/common/libimage/load.go @@ -6,8 +6,6 @@ import ( "context" "errors" "fmt" - "strings" - "syscall" "time" "github.com/sirupsen/logrus" @@ -112,11 +110,6 @@ func (r *Runtime) Load(ctx context.Context, path string, options *LoadOptions) ( return loadedImages, err } logrus.Debugf("Error loading %s (%s): %v", path, transportName, err) - // Surface ENOSPC immediately — no point trying other formats. - // The error arrives as a string from a subprocess, so check the message. - if strings.Contains(err.Error(), "no space left on device") { - return nil, fmt.Errorf("loading image: %w", syscall.ENOSPC) - } loadErrors = append(loadErrors, fmt.Errorf("%s: %v", transportName, err)) }