temporalio/temporal · error

decoding WorkerDeploymentMemo failed: Memo or it's fields ar

Error message

decoding WorkerDeploymentMemo failed: Memo or it's fields are nil

What it means

DecodeWorkerDeploymentMemo converts the memo payload stored on a WorkerDeployment workflow into a deploymentspb.WorkerDeploymentWorkflowMemo struct. It rejects calls where the memo or its Fields map is nil because there is no protobuf payload to decode, returning this sentinel-style error. The error indicates the deployment workflow was created without the required memo field (WorkerDeploymentMemoField).

Source

Thrown at service/worker/workerdeployment/util.go:170

// GenerateDeploymentWorkflowID is a helper that generates a system accepted
// workflowID which are used in our Worker Deployment workflows
func GenerateDeploymentWorkflowID(deploymentName string) string {
	return worker_versioning.WorkerDeploymentWorkflowIDPrefix + worker_versioning.WorkerDeploymentVersionDelimiter + deploymentName
}

// GenerateVersionWorkflowID is a helper that generates a system accepted
// workflowID which are used in our Worker Deployment Version workflows
func GenerateVersionWorkflowID(deploymentName string, buildID string) string {
	versionString := worker_versioning.ExternalWorkerDeploymentVersionToString(&deploymentpb.WorkerDeploymentVersion{
		DeploymentName: deploymentName,
		BuildId:        buildID,
	})
	return worker_versioning.WorkerDeploymentVersionWorkflowIDPrefix + worker_versioning.WorkerDeploymentVersionDelimiter + versionString
}

func DecodeWorkerDeploymentMemo(memo *commonpb.Memo) (*deploymentspb.WorkerDeploymentWorkflowMemo, error) {
	if memo == nil || memo.Fields == nil {
		return nil, errors.New("decoding WorkerDeploymentMemo failed: Memo or it's fields are nil")
	}
	var workerDeploymentWorkflowMemo deploymentspb.WorkerDeploymentWorkflowMemo
	err := sdk.PreferProtoDataConverter.FromPayload(memo.Fields[WorkerDeploymentMemoField], &workerDeploymentWorkflowMemo)
	if err != nil {
		return nil, err
	}
	return &workerDeploymentWorkflowMemo, nil
}

func getSafeDurationConfig(ctx workflow.Context, id string, unsafeGetter func() time.Duration, defaultValue time.Duration) (time.Duration, error) {
	get := func(_ workflow.Context) any {
		return unsafeGetter()
	}
	var value time.Duration
	if err := workflow.MutableSideEffect(ctx, id, get, durationEq).Get(&value); err != nil {
		return defaultValue, err
	}
	return value, nil

View on GitHub (pinned to bde624efd1)

Solutions

  1. Ensure the deployment workflow was created with a memo containing the WorkerDeploymentMemoField key before describing/decoding
  2. Check the memo key matches the exported WorkerDeploymentMemoField constant (schema/version drift between writers and readers)
  3. Nil-check memo and fields at the call site and surface a friendlier message, or fall back to a default empty memo struct
  4. If the deployment is corrupt/legacy, recreate the deployment version with a current SDK/CLI

Example fix

// before
memo, _ := resp.GetWorkflowExecutionInfo().GetMemo(), nil
deploymentMemo, err := workerdeployment.DecodeWorkerDeploymentMemo(memo)
// after
info := resp.GetWorkflowExecutionInfo()
if info.GetMemo() == nil || info.GetMemo().GetFields()[workerdeployment.WorkerDeploymentMemoField] == nil {
    return errors.New("deployment workflow has no deployment memo; it may have been created by an incompatible client")
}
deploymentMemo, err := workerdeployment.DecodeWorkerDeploymentMemo(info.GetMemo())
Defensive patterns

Strategy: validation

Validate before calling

func hasDeploymentMemo(memo *commonpb.Memo) bool {
    return memo != nil && memo.GetFields()[workerdeployment.WorkerDeploymentMemoField] != nil
}
// call DecodeWorkerDeploymentMemo only if hasDeploymentMemo(memo)

Type guard

func safeDecode(memo *commonpb.Memo) (*deploymentspb.WorkerDeploymentWorkflowMemo, error) {
    if memo == nil || memo.GetFields() == nil {
        return nil, errors.New("memo is nil; deployment workflow may predate memo support")
    }
    return workerdeployment.DecodeWorkerDeploymentMemo(memo)
}

Try / catch

memo, err := decode(m)
if err != nil {
    if strings.Contains(err.Error(), "Memo or it's fields are nil") {
        return handleMissingMemo(m) // fallback path
    }
    return err
}

Prevention

When it happens

Trigger: Calling DecodeWorkerDeploymentMemo with a nil *commonpb.Memo, or a Memo whose Fields map is nil/empty — e.g. the memo fetched via DescribeWorkflowExecution for a deployment whose workflow was started without a memo, or with the wrong memo key.

Common situations: Deployments created by older SDKs or CLI paths that did not set the WorkerDeploymentMemoField memo; user-modified workflow memos; describing a deployment right after a failed create; tests constructing workflows without memos.

Related errors


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