{"record":{"id":"263e20a1236a3589","repo":"temporalio/temporal","slug":"lcaitem-is-nil","errorCode":null,"errorMessage":"lcaItem is nil","messagePattern":"lcaItem is nil","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/versionhistory/version_history.go","lineNumber":204,"sourceCode":"\tversionHistoryItems []*historyspb.VersionHistoryItem,\n\tinitialFailoverVersion int64,\n\tfailoverVersionIncrement int64,\n) (localItems []*historyspb.VersionHistoryItem, remoteItems []*historyspb.VersionHistoryItem) {\n\tfor i, versionHistoryItem := range slices.Backward(versionHistoryItems) {\n\t\tif versionHistoryItem.Version%failoverVersionIncrement == initialFailoverVersion {\n\t\t\treturn versionHistoryItems[:i+1], versionHistoryItems[i+1:]\n\t\t}\n\t}\n\treturn nil, versionHistoryItems\n}\n\n// IsLCAVersionHistoryItemAppendable checks if a LCA VersionHistoryItem is appendable.\nfunc IsLCAVersionHistoryItemAppendable(v *historyspb.VersionHistory, lcaItem *historyspb.VersionHistoryItem) bool {\n\tif len(v.Items) == 0 {\n\t\tpanic(\"version history not initialized\")\n\t}\n\tif lcaItem == nil {\n\t\tpanic(\"lcaItem is nil\")\n\t}\n\n\treturn IsEqualVersionHistoryItem(v.Items[len(v.Items)-1], lcaItem)\n}\n\n// GetFirstVersionHistoryItem return the first VersionHistoryItem.\nfunc GetFirstVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {\n\tif len(v.Items) == 0 {\n\t\treturn nil, serviceerror.NewInternal(\"version history is empty.\")\n\t}\n\treturn CopyVersionHistoryItem(v.Items[0]), nil\n}\n\n// GetLastVersionHistoryItem return the last VersionHistoryItem.\nfunc GetLastVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {\n\treturn getLastVersionHistoryItem(v.Items)\n}\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/versionhistory/version_history.go#L186-L222","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlca := versionhistory.FindLCAVersionHistoryItem(vh, target)\nappendable := versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca) // panics when lca nil\n// after\nlca := versionhistory.FindLCAVersionHistoryItem(vh, target)\nif lca == nil {\n    return fmt.Errorf(\"no LCA found between version histories\")\n}\nappendable := versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca)","handlingStrategy":"validation","validationCode":"func appendableSafe(vh *historyspb.VersionHistory, lca *historyspb.VersionHistoryItem) (bool, error) {\n    if lca == nil { return false, errors.New(\"no LCA version history item\") }\n    if len(vh.GetItems()) == 0 { return false, errors.New(\"version history not initialized\") }\n    return versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca), nil\n}","typeGuard":"func hasLCA(lca *historyspb.VersionHistoryItem) bool { return lca != nil }","tryCatchPattern":"// compute LCA and handle nil explicitly before the appendability check\nlca := versionhistory.FindLCAVersionHistoryItem(vh, other)\nif lca == nil { return internalErr }\nreturn versionhistory.IsLCAVersionHistoryItemAppendable(vh, lca), nil","preventionTips":["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"],"tags":["persistence","panic","version-history","nil-pointer"],"backgroundTag":"nil-lca-version-history-item","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}