temporalio/temporal · error

version history cannot be null

Error message

version history cannot be null

What it means

NewVersionHistories wraps an initial VersionHistory into the VersionHistories container used by execution state; it panics immediately if the input VersionHistory is nil. A VersionHistories with no first branch is structurally meaningless (CurrentVersionHistoryIndex would point at nothing), so the constructor enforces the non-nil invariant at creation time.

Source

Thrown at common/persistence/versionhistory/version_histories.go:11

package versionhistory

import (
	"go.temporal.io/api/serviceerror"
	historyspb "go.temporal.io/server/api/history/v1"
)

// NewVersionHistories create a new instance of VersionHistories.
func NewVersionHistories(versionHistory *historyspb.VersionHistory) *historyspb.VersionHistories {
	if versionHistory == nil {
		panic("version history cannot be null")
	}

	return &historyspb.VersionHistories{
		CurrentVersionHistoryIndex: 0,
		Histories:                  []*historyspb.VersionHistory{versionHistory},
	}
}

// Copy VersionHistories.
func CopyVersionHistories(h *historyspb.VersionHistories) *historyspb.VersionHistories {
	var histories []*historyspb.VersionHistory
	for _, history := range h.Histories {
		histories = append(histories, CopyVersionHistory(history))
	}

	return &historyspb.VersionHistories{
		CurrentVersionHistoryIndex: h.CurrentVersionHistoryIndex,
		Histories:                  histories,

View on GitHub (pinned to bde624efd1)

Solutions

  1. Pass a freshly constructed branch, e.g. NewVersionHistories(NewVersionHistory(NewVersionHistoryItem(firstEventID, firstVersion))), when bootstrapping a new execution.
  2. Guard the input: if the source VersionHistory is nil, create a default initial version history instead of passing nil through.
  3. Check that the proto field you sourced the history from (branch token / mutable state) is actually populated before calling.
  4. Upgrade/sync cluster versions if nil histories come from older persisted state.

Example fix

// before
histories := versionhistory.NewVersionHistories(mutableState.GetVersionHistories().GetHistories()[idx]) // may be nil
// after
vh := mutableState.GetVersionHistories().GetHistories()[idx]
if vh == nil {
    vh = versionhistory.NewVersionHistory(versionhistory.NewVersionHistoryItem(1, 1))
}
histories := versionhistory.NewVersionHistories(vh)
Defensive patterns

Strategy: validation

Validate before calling

func safeNewVersionHistories(vh *historyspb.VersionHistory) (*historyspb.VersionHistories, error) {
    if vh == nil || len(vh.GetItems()) == 0 {
        return nil, errors.New("input version history is nil or empty")
    }
    return versionhistory.NewVersionHistories(vh), nil
}

Type guard

func versionHistoryExists(vh *historyspb.VersionHistory) bool { return vh != nil && len(vh.GetItems()) > 0 }

Try / catch

// Go panics cannot be caught as errors; guard the input instead of recovering
if vh == nil { return nil, internalErr }
histories := versionhistory.NewVersionHistories(vh)

Prevention

When it happens

Trigger: Calling versionhistory.NewVersionHistories(nil), typically when the caller obtained the VersionHistory from a proto field (e.g. mutableState.VersionHistories or a branch token lookup) that was unset, or when building execution info from partially decoded state.

Common situations: Replaying history where the versioning info proto is missing/old (version skew between clusters); test helpers passing a nil branch; constructing WorkflowExecutionInfo from a mutable state blob that predates versioning.

Related errors


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