temporalio/temporal · error
lcaItem is nil
Error message
lcaItem is nil
What it means
IsLCAVersionHistoryItemAppendable panics when the supplied LCA item pointer is nil. The function compares the history's last item to the LCA item via IsEqualVersionHistoryItem; a nil LCA means the caller's fork/merge computation failed to produce a common ancestor, which is a caller bug, so the guard fails fast before a nil dereference would occur inside the comparison.
Source
Thrown at common/persistence/versionhistory/version_history.go:204
versionHistoryItems []*historyspb.VersionHistoryItem,
initialFailoverVersion int64,
failoverVersionIncrement int64,
) (localItems []*historyspb.VersionHistoryItem, remoteItems []*historyspb.VersionHistoryItem) {
for i, versionHistoryItem := range slices.Backward(versionHistoryItems) {
if versionHistoryItem.Version%failoverVersionIncrement == initialFailoverVersion {
return versionHistoryItems[:i+1], versionHistoryItems[i+1:]
}
}
return nil, versionHistoryItems
}
// IsLCAVersionHistoryItemAppendable checks if a LCA VersionHistoryItem is appendable.
func IsLCAVersionHistoryItemAppendable(v *historyspb.VersionHistory, lcaItem *historyspb.VersionHistoryItem) bool {
if len(v.Items) == 0 {
panic("version history not initialized")
}
if lcaItem == nil {
panic("lcaItem is nil")
}
return IsEqualVersionHistoryItem(v.Items[len(v.Items)-1], lcaItem)
}
// GetFirstVersionHistoryItem return the first VersionHistoryItem.
func GetFirstVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {
if len(v.Items) == 0 {
return nil, serviceerror.NewInternal("version history is empty.")
}
return CopyVersionHistoryItem(v.Items[0]), nil
}
// GetLastVersionHistoryItem return the last VersionHistoryItem.
func GetLastVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {
return getLastVersionHistoryItem(v.Items)
}
View on GitHub (pinned to bde624efd1)
Solutions
- Check lcaItem != nil before calling; if nil, treat the history as not appendable or return an internal error instead of panicking.
- Ensure the LCA is computed with FindLCAVersionHistoryItem on the same VersionHistories instance the branch belongs to.
- If histories legitimately share no ancestor, route through the non-LCA appendability path rather than fabricating a nil LCA.
- Add regression tests for fork/merge cases that previously produced nil LCA.
Example fix
// before
lca := versionhistory.FindLCAVersionHistoryItem(vh, target)
appendable := versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca) // panics when lca nil
// after
lca := versionhistory.FindLCAVersionHistoryItem(vh, target)
if lca == nil {
return fmt.Errorf("no LCA found between version histories")
}
appendable := versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca) Defensive patterns
Strategy: validation
Validate before calling
func appendableSafe(vh *historyspb.VersionHistory, lca *historyspb.VersionHistoryItem) (bool, error) {
if lca == nil { return false, errors.New("no LCA version history item") }
if len(vh.GetItems()) == 0 { return false, errors.New("version history not initialized") }
return versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca), nil
} Type guard
func hasLCA(lca *historyspb.VersionHistoryItem) bool { return lca != nil } Try / catch
// compute LCA and handle nil explicitly before the appendability check
lca := versionhistory.FindLCAVersionHistoryItem(vh, other)
if lca == nil { return internalErr }
return versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca), nil Prevention
- Treat nil LCA from FindLCA* as a real condition, not something to pass through
- Cover fork/merge edge cases (disjoint branches) with regression tests
- Keep LCA computation and appendability check on the same histories instance
When it happens
Trigger: Calling versionhistory.IsLCAVersionHistoryItemAppendable(v, nil), usually when an earlier call like FindLCAVersionHistoryItem / GetLCAVersionHistoryItem returned nil because no common-ancestor item was found between two branches.
Common situations: Comparing histories from two clusters/namespaces whose branch tokens diverged with no shared root; test code passing nil LCA to exercise appendability; bug in prepareBranch/getBranchToAppend-style logic that drops the LCA before the check.
Related errors
- version history cannot be null
- version history not initialized
- invalid version history item event ID: %v, version: %v
- unable to decode cassandra serial consistency: %v
- invalid task schema version
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/263e20a1236a3589.
Report an issue: GitHub.