Repository: cadence-workflow/cadence (2025-08-20T04:47:04Z)

Issue:
CLI start workflow help command retry flags default values is incorrect
### Description
Trying to run the following cadence cli command:
```
cadence  --domain my-domain workflow start --tl helloWorldGroup --wt helloWorldWorkflow --et 60 --retry_attempts 0
``` 
Shows the following error:
```
MaximumAttempts and ExpirationIntervalInSeconds are both 0. At least one of them must be specified.
```

There are two things that made this message unclear:
1- It wasn't clear that the flag for `ExpirationIntervalInSeconds` is `--retry_expiration`
2- Second issue is that the help command mentioned that the default value for  `retry_attempts` is `0`  & `retry_expiration` is `0` which may not be the case since it would show this error by default

### Expected behaviour
A more descriptive error is need describing the exact flags on the CLI. Also the default value needs to be corrected or a correct description for when it is defaulted to 0 is needed.



Discussion:

Patch:
diff --git a/tools/cli/flags.go b/tools/cli/flags.go
index 15cd4d0fb38..9fcfa26adbf 100644
--- a/tools/cli/flags.go
+++ b/tools/cli/flags.go
@@ -412,11 +412,11 @@ func getFlagsForStart() []cli.Flag {
 		},
 		&cli.IntFlag{
 			Name:  FlagRetryExpiration,
-			Usage: "Optional retry expiration in seconds. If set workflow will be retried for the specified period of time.",
+			Usage: "Optional retry expiration in seconds. If set workflow will be retried for the specified period of time. retry_attempts and retry_expiration must not both be 0.",
 		},
 		&cli.IntFlag{
 			Name:  FlagRetryAttempts,
-			Usage: "Optional retry attempts. If set workflow will be retried the specified amount of times.",
+			Usage: "Optional retry attempts. If set workflow will be retried the specified amount of times. retry_attempts and retry_expiration must not both be 0.",
 		},
 		&cli.IntFlag{
 			Name:  FlagRetryInterval,
@@ -426,7 +426,7 @@ func getFlagsForStart() []cli.Flag {
 		&cli.Float64Flag{
 			Name:  FlagRetryBackoff,
 			Value: 1.0,
-			Usage: "Optional retry backoff coeficient. Must be or equal or greater than 1.",
+			Usage: "Optional retry backoff coefficient. Must be or equal or greater than 1.",
 		},
 		&cli.IntFlag{
 			Name:  FlagRetryMaxInterval,
diff --git a/tools/cli/workflow_commands.go b/tools/cli/workflow_commands.go
index 04e6a431b0d..6b5ac1500ea 100644
--- a/tools/cli/workflow_commands.go
+++ b/tools/cli/workflow_commands.go
@@ -322,7 +322,7 @@ func startWorkflowHelper(c *cli.Context, shouldPrintProgress bool) error {
 		resp, err := serviceClient.StartWorkflowExecution(tcCtx, startRequest)
 
 		if err != nil {
-			return commoncli.Problem("Failed to create workflow.", err)
+			return commoncli.Problem("Failed to create workflow.", replaceRetryPropertiesInErrorMessageWithRetryArguments(err))
 		}
 		fmt.Printf("Started Workflow Id: %s, run Id: %s\n", wid, resp.GetRunID())
 		return nil
@@ -337,7 +337,7 @@ func startWorkflowHelper(c *cli.Context, shouldPrintProgress bool) error {
 		resp, err := serviceClient.StartWorkflowExecution(tcCtx, startRequest)
 
 		if err != nil {
-			return commoncli.Problem("Failed to run workflow.", err)
+			return commoncli.Problem("Failed to run workflow.", replaceRetryPropertiesInErrorMessageWithRetryArguments(err))
 		}
 
 		// print execution summary
@@ -366,6 +366,21 @@ func startWorkflowHelper(c *cli.Context, shouldPrintProgress bool) error {
 	return startFn()
 }
 
+func replaceRetryPropertiesInErrorMessageWithRetryArguments(err error) error {
+	errMsg := err.Error()
+	// This mapping is built based on the implementation of the function constructStartWorkflowRequest
+	for requestField, cliFlag := range map[string]string{
+		"InitialIntervalInSeconds":    FlagRetryInterval,
+		"BackoffCoefficient":          FlagRetryBackoff,
+		"MaximumIntervalInSeconds":    FlagRetryMaxInterval,
+		"MaximumAttempts":             FlagRetryAttempts,
+		"ExpirationIntervalInSeconds": FlagRetryExpiration,
+	} {
+		errMsg = strings.ReplaceAll(errMsg, requestField, cliFlag)
+	}
+	return errors.New(errMsg)
+}
+
 func constructStartWorkflowRequest(c *cli.Context) (*types.StartWorkflowExecutionRequest, error) {
 	domain, err := getRequiredOption(c, FlagDomain)
 	if err != nil {


Test patch:
diff --git a/tools/cli/workflow_commands_test.go b/tools/cli/workflow_commands_test.go
index 64fceadb3eb..44e8d679778 100644
--- a/tools/cli/workflow_commands_test.go
+++ b/tools/cli/workflow_commands_test.go
@@ -2088,6 +2088,74 @@ func Test_NewTest(t *testing.T) {
 
 }
 
+func Test_StartWorkflowHelper_RetryErrorMapping(t *testing.T) {
+	requiredArguments := []clitest.CliArgument{
+		clitest.StringArgument(FlagDomain, "test-domain"),
+		clitest.StringArgument(FlagTaskList, "test-tasklist"),
+		clitest.StringArgument(FlagWorkflowType, "test-workflow-type"),
+		clitest.StringArgument(FlagExecutionTimeout, "10"),
+	}
+	tests := []struct {
+		cliArguments          []clitest.CliArgument
+		respondedErrorMessage string
+		expectedErrorMessage  string
+	}{
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 1), clitest.IntArgument(FlagRetryInterval, -1)),
+			"InitialIntervalInSeconds must be greater than 0 on retry policy.",
+			"retry_interval must be greater than 0 on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 1), clitest.IntArgument(FlagRetryBackoff, -1)),
+			"BackoffCoefficient cannot be less than 1 on retry policy.",
+			"retry_backoff cannot be less than 1 on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 1), clitest.IntArgument(FlagRetryMaxInterval, -1)),
+			"MaximumIntervalInSeconds cannot be less than 0 on retry policy.",
+			"retry_max_interval cannot be less than 0 on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 1), clitest.IntArgument(FlagRetryInterval, 2), clitest.IntArgument(FlagRetryMaxInterval, -1)),
+			"MaximumIntervalInSeconds cannot be less than InitialIntervalInSeconds on retry policy.",
+			"retry_max_interval cannot be less than retry_interval on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, -1)),
+			"MaximumAttempts cannot be less than 0 on retry policy.",
+			"retry_attempts cannot be less than 0 on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 1), clitest.IntArgument(FlagRetryExpiration, -1)),
+			"ExpirationIntervalInSeconds cannot be less than 0 on retry policy.",
+			"retry_expiration cannot be less than 0 on retry policy.",
+		},
+		{
+			append(requiredArguments, clitest.IntArgument(FlagRetryAttempts, 0), clitest.IntArgument(FlagRetryBackoff, 0)),
+			"MaximumAttempts and ExpirationIntervalInSeconds are both 0. At least one of them must be specified.",
+			"retry_attempts and retry_expiration are both 0. At least one of them must be specified.",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.expectedErrorMessage, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+
+			mockServiceClient := frontend.NewMockClient(ctrl)
+			mockServiceClient.EXPECT().
+				StartWorkflowExecution(gomock.Any(), gomock.Any()).
+				Return(nil, errors.New(tc.respondedErrorMessage))
+
+			app := NewCliApp(&clientFactoryMock{serverFrontendClient: mockServiceClient})
+
+			ctx := clitest.NewCLIContext(t, app, tc.cliArguments...)
+			err := startWorkflowHelper(ctx, false)
+			assert.ErrorContains(t, err, tc.expectedErrorMessage)
+
+		})
+	}
+}
+
 func Test_ProcessSearchAttr(t *testing.T) {
 	app := NewCliApp(&clientFactoryMock{})
 	set := flag.NewFlagSet("test", 0)