{"record":{"id":"f04cdf32fc928961","repo":"temporalio/temporal","slug":"invalid-version-history-item-event-id-v-version","errorCode":null,"errorMessage":"invalid version history item event ID: %v, version: %v","messagePattern":"invalid version history item event ID: (.+?), version: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/versionhistory/version_history_item.go","lineNumber":12,"sourceCode":"package versionhistory\n\nimport (\n\t\"fmt\"\n\n\thistoryspb \"go.temporal.io/server/api/history/v1\"\n)\n\n// NewVersionHistoryItem create a new instance of VersionHistoryItem.\nfunc NewVersionHistoryItem(eventID int64, version int64) *historyspb.VersionHistoryItem {\n\tif eventID < 0 || version < 0 {\n\t\tpanic(fmt.Sprintf(\"invalid version history item event ID: %v, version: %v\", eventID, version))\n\t}\n\n\treturn &historyspb.VersionHistoryItem{EventId: eventID, Version: version}\n}\n\n// CopyVersionHistoryItem create a new instance of VersionHistoryItem.\nfunc CopyVersionHistoryItem(item *historyspb.VersionHistoryItem) *historyspb.VersionHistoryItem {\n\treturn NewVersionHistoryItem(item.EventId, item.Version)\n}\n\n// IsEqualVersionHistoryItem checks whether version history items are equal\nfunc IsEqualVersionHistoryItem(item1 *historyspb.VersionHistoryItem, item2 *historyspb.VersionHistoryItem) bool {\n\treturn item1.EventId == item2.EventId && item1.Version == item2.Version\n}\n\n// IsEqualVersionHistoryItems checks whether version history items are equal\nfunc IsEqualVersionHistoryItems(items1 []*historyspb.VersionHistoryItem, items2 []*historyspb.VersionHistoryItem) bool {\n\tif len(items1) != len(items2) {","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/versionhistory/version_history_item.go#L1-L30","documentation":"NewVersionHistoryItem constructs a VersionHistoryItem and panics if either eventID or version is negative. Event IDs and transition versions in history branching are monotonically non-negative counters; a negative value means an upstream computation (e.g. event ID arithmetic during duplication or branch fork) underflowed, so the constructor rejects it as an invariant violation.","triggerScenarios":"Calling versionhistory.NewVersionHistoryItem(eventID, version) with eventID < 0 or version < 0 — typically from CopyVersionHistoryItem on a corrupt source item, or from arithmetic like previousItem.EventId - 1 / version decrements that go below zero.","commonSituations":"Duplicating history items until an LCA where the LCA is the first item (EventId 1) causing EventId-1 = 0 or negative offsets; underflow when subtracting event ID deltas after history truncation; corrupted mutable state with negative IDs loaded from storage.","solutions":["Clamp computed event IDs/versions to >= 0 (or >= 1 for real events) before calling NewVersionHistoryItem.","In duplicate-until-LCA logic, stop iteration when reaching the LCA item instead of computing offset items below its ID.","Validate decoded mutable state items before copying; reject or repair negative IDs at load time.","Add unit tests for first-item LCA duplication cases (TestDuplicateUntilLCAItem_Failure covers the panic path)."],"exampleFix":"// before\nnewItem := versionhistory.NewVersionHistoryItem(lcaItem.EventId-1, lcaItem.Version) // can go negative\n// after\nnewEventID := lcaItem.EventId - 1\nif newEventID < 0 {\n    return ErrInvalidVersionHistoryItem\n}\nnewItem := versionhistory.NewVersionHistoryItem(newEventID, lcaItem.Version)","handlingStrategy":"validation","validationCode":"func safeNewItem(eventID, version int64) (*historyspb.VersionHistoryItem, error) {\n    if eventID < 0 || version < 0 {\n        return nil, fmt.Errorf(\"invalid version history item event ID: %v, version: %v\", eventID, version)\n    }\n    return versionhistory.NewVersionHistoryItem(eventID, version), nil\n}","typeGuard":"func isValidItemInput(eventID, version int64) bool { return eventID >= 0 && version >= 0 }","tryCatchPattern":"// clamp/validate arithmetic results before constructing items\nnewID := lcaItem.EventId - 1\nif newID < 0 { return ErrInvalidVersionHistoryItem }\nitem := versionhistory.NewVersionHistoryItem(newID, lcaItem.Version)","preventionTips":["Guard all event-ID/version arithmetic for underflow, especially LCA-offset duplication","Validate decoded mutable state items for negative IDs at load","Add tests for first-item-LCA duplication paths"],"tags":["persistence","panic","version-history","invalid-argument"],"backgroundTag":"invalid-version-history-item","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}