temporalio/temporal · error

invalid WorkflowRetryPolicy on ContinueAsNewWorkflowExecutio

Error message

invalid WorkflowRetryPolicy on ContinueAsNewWorkflowExecutionCommand: %w. WorkflowType=%s TaskQueue=%s

What it means

The ContinueAsNew command's WorkflowRetryPolicy fails validateWorkflowRetryPolicy (e.g. invalid backoff coefficients, negative intervals, non-positive maximum attempts format). The workflow task fails with BAD_* cause and this wrapped message.

Source

Thrown at service/history/api/command_attr_validator.go:448

	if err := timestamp.ValidateAndCapProtoDuration(attributes.GetBackoffStartInterval()); err != nil {
		return failedCause, serviceerror.NewInvalidArgumentf("Invalid BackoffStartInterval on ContinueAsNewWorkflowExecutionCommand: %v. WorkflowType=%s TaskQueue=%s", err, wfType, attributes.TaskQueue)
	}

	if attributes.GetWorkflowRunTimeout().AsDuration() == 0 {
		attributes.WorkflowRunTimeout = executionInfo.WorkflowRunTimeout
	}

	if attributes.GetWorkflowTaskTimeout().AsDuration() == 0 {
		attributes.WorkflowTaskTimeout = executionInfo.DefaultWorkflowTaskTimeout
	}

	attributes.WorkflowRunTimeout = durationpb.New(overrideWorkflowRunTimeout(attributes.GetWorkflowRunTimeout().AsDuration(), executionInfo.GetWorkflowExecutionTimeout().AsDuration()))

	attributes.WorkflowTaskTimeout = durationpb.New(overrideWorkflowTaskTimeout(namespaceName, attributes.GetWorkflowTaskTimeout().AsDuration(), attributes.GetWorkflowRunTimeout().AsDuration(), v.config.DefaultWorkflowTaskTimeout))

	if err := v.validateWorkflowRetryPolicy(namespaceName, attributes.RetryPolicy); err != nil {
		return failedCause, fmt.Errorf("invalid WorkflowRetryPolicy on ContinueAsNewWorkflowExecutionCommand: %w. WorkflowType=%s TaskQueue=%s", err, wfType, attributes.TaskQueue)
	}

	if err := v.searchAttributesValidator.Validate(attributes.GetSearchAttributes(), namespaceName.String()); err != nil {
		return enumspb.WORKFLOW_TASK_FAILED_CAUSE_BAD_SEARCH_ATTRIBUTES, fmt.Errorf("invalid SearchAttributes on ContinueAsNewWorkflowExecutionCommand: %w. WorkflowType=%s TaskQueue=%s", err, wfType, attributes.TaskQueue)
	}

	return enumspb.WORKFLOW_TASK_FAILED_CAUSE_UNSPECIFIED, nil
}

func (v *CommandAttrValidator) ValidateStartChildExecutionAttributes(
	namespaceID namespace.ID,
	targetNamespaceID namespace.ID,
	targetNamespace namespace.Name,
	attributes *commandpb.StartChildWorkflowExecutionCommandAttributes,
	parentInfo *persistencespb.WorkflowExecutionInfo,
	defaultWorkflowTaskTimeoutFn dynamicconfig.DurationPropertyFnWithNamespaceFilter,
) (enumspb.WorkflowTaskFailedCause, error) {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Use SDK RetryPolicy builders / client defaults instead of hand-building the proto
  2. Validate InitialInterval > 0, BackoffCoefficient >= 1, MaximumInterval >= InitialInterval
  3. Check numeric fields for zero-values from unpopulated config structs

Example fix

// before
policy := &workflowsvc.RetryPolicy{MaximumAttempts: 3, InitialInterval: durationpb.New(0)}
// after
policy := &workflowsvc.RetryPolicy{MaximumAttempts: 3, InitialInterval: durationpb.New(time.Second), BackoffCoefficient: 2}
Defensive patterns

Strategy: validation

Validate before calling

func validRetryPolicy(p *workflowsvc.RetryPolicy) bool { return p.GetInitialInterval().AsDuration() > 0 && p.GetBackoffCoefficient() >= 1 && (p.GetMaximumInterval() == nil || p.GetMaximumInterval().AsDuration() >= p.GetInitialInterval().AsDuration()) }

Type guard

func hasRetryPolicy(a *workflowsvc.ContinueAsNewWorkflowExecutionCommandAttributes) *workflowsvc.RetryPolicy { return a.GetRetryPolicy() }

Try / catch

err := validator.ValidateContinueAsNewWorkflowExecutionAttributes(...); if err != nil { return err } — callers see workflow task failure; inspect workflow history event failure details

Prevention

When it happens

Trigger: Workflow invokes ContinueAsNew with a RetryPolicy option containing invalid values (zero/negative InitialInterval, coefficient < 1, invalid MaximumAttempts/Expiration combination).

Common situations: Copying retry policy from config with unit mistakes (ms vs s); setting MaximumInterval smaller than InitialInterval; coefficient defaults missing on hand-built policies.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/89f8b58e01cbe643. Report an issue: GitHub.