temporalio/temporal · error

errInvalidWorkflowExecutionTimeoutSeconds

errInvalidWorkflowExecutionTimeoutSeconds

Error message

%w cause: %v

What it means

The CHASM request validator validates workflow execution timeout via timestamp.ValidateAndCapProtoDuration; on failure it wraps the cause with errInvalidWorkflowExecutionTimeoutSeconds. This means the WorkflowExecutionTimeout in the start request is invalid (negative, non-integer seconds, or exceeds the cap).

Source

Thrown at chasm/lib/workflow/validator.go:73

		return ErrWorkflowIDNotSet
	}
	if len(workflowID) > v.config.maxIDLengthLimit() {
		return serviceerror.NewInvalidArgumentf("WorkflowId exceeds maximum allowed length (%d/%d)", len(workflowID), v.config.maxIDLengthLimit())
	}
	return nil
}

type StartWorkflowTimeoutLikeRequest interface {
	GetWorkflowExecutionTimeout() *durationpb.Duration
	GetWorkflowRunTimeout() *durationpb.Duration
	GetWorkflowTaskTimeout() *durationpb.Duration
}

func (v *RequestValidator) ValidateWorkflowTimeouts(
	request StartWorkflowTimeoutLikeRequest,
) error {
	if err := timestamp.ValidateAndCapProtoDuration(request.GetWorkflowExecutionTimeout()); err != nil {
		return fmt.Errorf("%w cause: %v", errInvalidWorkflowExecutionTimeoutSeconds, err)
	}

	if err := timestamp.ValidateAndCapProtoDuration(request.GetWorkflowRunTimeout()); err != nil {
		return fmt.Errorf("%w cause: %v", errInvalidWorkflowRunTimeoutSeconds, err)
	}

	if err := timestamp.ValidateAndCapProtoDuration(request.GetWorkflowTaskTimeout()); err != nil {
		return fmt.Errorf("%w cause: %v", errInvalidWorkflowTaskTimeoutSeconds, err)
	}

	return nil
}

func (v *RequestValidator) ValidateRetryPolicy(namespaceName string, retryPolicy *commonpb.RetryPolicy) error {
	if retryPolicy == nil {
		// By default, if the user does not explicitly set a retry policy for a Workflow, do not perform any retries.
		return nil
	}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Set WorkflowExecutionTimeout to a whole-number-of-seconds duration > 0 within the server cap (timestamp.ValidateAndCapProtoDuration rules)
  2. Fix duration computation so no fractional/negative values reach the request (e.g. time.Duration truncation to seconds)
  3. Check dynamic config caps on execution timeout for the namespace and align client values
  4. Clear the field (leave zero/unset) if no execution timeout is intended

Example fix

// before
req.WorkflowExecutionTimeout = durationpb.New(-5 * time.Second)
// after
req.WorkflowExecutionTimeout = durationpb.New(3600 * time.Second)
Defensive patterns

Strategy: validation

Validate before calling

func validTimeout(d *durationpb.Duration) bool {
    if d == nil { return true }
    dur := d.AsDuration()
    return dur > 0 && dur%time.Second == 0
}
// call before StartWorkflowExecution:
if !validTimeout(req.WorkflowExecutionTimeout) { /* fix or clear field */ }

Try / catch

// Go
if err := validator.ValidateWorkflowTimeouts(req); err != nil {
    if errors.Is(err, errInvalidWorkflowExecutionTimeoutSeconds) {
        return status.Errorf(codes.InvalidArgument, "execution timeout: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling a start-workflow-style API (e.g. StartWorkflowExecution or, via ValidateSignalWithStartRequest, SignalWithStart) with request.WorkflowExecutionTimeout set to a negative duration, sub-second value, or one exceeding the configured maximum.

Common situations: Clients computing timeouts in milliseconds and passing a duration with sub-second precision; accidental negative values from misconfigured deadline arithmetic; tests or scripts setting huge timeouts beyond the server cap.

Related errors


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