Only suggest --replace for commands that have the flag

formatError appended "or use --replace to instruct Podman to do so" to
every error wrapping storage.ErrDuplicateName, even for commands like
"podman manifest create" that have no --replace flag, telling users to
use a flag that does not exist.

Resolve the invoked command via ExecuteContextC and only add the hint
when that command actually defines a --replace flag.

Fixes: #24537
Signed-off-by: Salih Muhammed <root@lr0.org>
(cherry picked from commit 20eae39b13)
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Salih Muhammed 2026-07-03 01:35:16 +03:00 committed by Paul Holzinger
parent 32e141b15c
commit d1b65d35de
No known key found for this signature in database
GPG key ID: EB145DD938A3CAF2
2 changed files with 40 additions and 6 deletions

View file

@ -136,7 +136,7 @@ func init() {
}
func Execute() {
if err := rootCmd.ExecuteContext(registry.Context()); err != nil {
if cmd, err := rootCmd.ExecuteContextC(registry.Context()); err != nil {
if registry.GetExitCode() == 0 {
registry.SetExitCode(define.ExecErrorCodeGeneric)
}
@ -145,7 +145,7 @@ func Execute() {
fmt.Fprintln(os.Stderr, "Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM")
}
}
fmt.Fprintln(os.Stderr, formatError(err))
fmt.Fprintln(os.Stderr, formatError(err, cmd))
}
_ = shutdown.Stop()
@ -726,7 +726,7 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
}
}
func formatError(err error) string {
func formatError(err error, cmd *cobra.Command) string {
var message string
switch {
case errors.Is(err, define.ErrOCIRuntime):
@ -737,7 +737,9 @@ func formatError(err error) string {
define.ErrOCIRuntime.Error(),
strings.TrimSuffix(err.Error(), ": "+define.ErrOCIRuntime.Error()),
)
case errors.Is(err, storage.ErrDuplicateName):
case errors.Is(err, storage.ErrDuplicateName) && cmd != nil && cmd.Flags().Lookup("replace") != nil:
// Only suggest --replace when the invoked command actually has
// the flag (e.g. "manifest create" does not).
message = fmt.Sprintf("Error: %s, or use --replace to instruct Podman to do so.", err.Error())
default:
if logrus.IsLevelEnabled(logrus.TraceLevel) {

View file

@ -6,12 +6,14 @@ import (
"strings"
"testing"
"github.com/spf13/cobra"
"go.podman.io/podman/v6/libpod/define"
"go.podman.io/storage"
)
func TestFormatError(t *testing.T) {
err := errors.New("unknown error")
output := formatError(err)
output := formatError(err, nil)
expected := fmt.Sprintf("Error: %v", err)
if output != expected {
@ -19,6 +21,36 @@ func TestFormatError(t *testing.T) {
}
}
func TestFormatErrorDuplicateName(t *testing.T) {
withReplace := &cobra.Command{Use: "create"}
withReplace.Flags().Bool("replace", false, "")
withoutReplace := &cobra.Command{Use: "create"}
err := fmt.Errorf("that name is already in use: %w", storage.ErrDuplicateName)
hint := "or use --replace to instruct Podman to do so."
tests := []struct {
name string
cmd *cobra.Command
wantHint bool
}{
{name: "command with --replace flag", cmd: withReplace, wantHint: true},
{name: "command without --replace flag", cmd: withoutReplace, wantHint: false},
{name: "no command", cmd: nil, wantHint: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output := formatError(err, tt.cmd)
if got := strings.Contains(output, hint); got != tt.wantHint {
t.Errorf("formatError() = %q, want hint contained: %v", output, tt.wantHint)
}
if !strings.Contains(output, err.Error()) {
t.Errorf("formatError() = %q, want the original error message included", output)
}
})
}
}
func TestIndentExamples(t *testing.T) {
tests := []struct {
name string
@ -65,7 +97,7 @@ func TestFormatOCIError(t *testing.T) {
expectedPrefix := "Error: "
expectedSuffix := "OCI runtime output"
err := fmt.Errorf("%s: %w", expectedSuffix, define.ErrOCIRuntime)
output := formatError(err)
output := formatError(err, nil)
if !strings.HasPrefix(output, expectedPrefix) {
t.Errorf("Expected \"%s\" to start with \"%s\"", output, expectedPrefix)