{"record":{"id":"d20b2f1ef7852805","repo":"temporalio/temporal","slug":"invalid-state","errorCode":null,"errorMessage":"invalid state","messagePattern":"invalid state","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"service/history/shard/context_impl.go","lineNumber":1142,"sourceCode":"}\n\nfunc (s *ContextImpl) getRangeIDLocked() int64 {\n\treturn s.shardInfo.GetRangeId()\n}\n\nfunc (s *ContextImpl) errorByState() error {\n\ts.stateLock.Lock()\n\tdefer s.stateLock.Unlock()\n\n\tswitch s.state {\n\tcase contextStateInitialized, contextStateAcquiring:\n\t\treturn ErrShardStatusUnknown\n\tcase contextStateAcquired:\n\t\treturn nil\n\tcase contextStateStopping, contextStateStopped:\n\t\treturn s.newShardClosedErrorWithShardID()\n\tdefault:\n\t\tpanic(\"invalid state\")\n\t}\n}\n\nfunc (s *ContextImpl) errorByNamespaceStateLocked(\n\tnamespaceName namespace.Name,\n\tworkflowID string,\n) error {\n\tif s.handoverTracker.IsInHandover(namespaceName, workflowID) {\n\t\treturn consts.ErrNamespaceHandover\n\t}\n\treturn nil\n}\n\nfunc (s *ContextImpl) generateTaskIDLocked() (int64, error) {\n\ttaskKey, err := s.taskKeyManager.generateTaskKey(tasks.CategoryTransfer)\n\tif err != nil {\n\t\treturn -1, err\n\t}","sourceCodeStart":1124,"sourceCodeEnd":1160,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/service/history/shard/context_impl.go#L1124-L1160","documentation":"ContextImpl.errorByState maps the shard context's lifecycle state to an error for callers during an operation: uninitialized/acquiring → ErrShardStatusUnknown, acquired → nil, stopping/stopped → shard-closed error. The default branch panics because every defined contextState should be covered; hitting it means the state variable holds an undefined value. It is an exhaustiveness invariant, not a caller-recoverable condition.","triggerScenarios":"A new contextState constant added to shard context without updating errorByState's switch; a race or memory bug corrupting s.state; direct writes to the state field somewhere bypassing the state machine transitions.","commonSituations":"Developing/forking temporal-server's shard controller and adding states (e.g. contextStateLoading) without updating all switches; concurrency bugs surfaced only under heavy shard ownership transfer load.","solutions":["Add the missing contextState case to errorByState, returning the appropriate error","Grep contextState* constants and audit every switch over s.state for exhaustiveness","Run the shard controller tests under -race to rule out unsynchronized state writes","If hit on a stock build, capture the shard ID and stack and file an issue with temporalio/temporal"],"exampleFix":"// before\ncase contextStateStopping, contextStateStopped:\n  return s.newShardClosedErrorWithShardID()\ndefault:\n  panic(\"invalid state\")\n// after\ncase contextStateStopping, contextStateStopped:\n  return s.newShardClosedErrorWithShardID()\ncase contextStateLoading: // newly added state\n  return ErrShardStatusUnknown\ndefault:\n  panic(\"invalid state\")","handlingStrategy":"validation","validationCode":"// On shard-context errors, check the returned sentinel errors instead of panics:\nerr := ctx.UpdateWorkflowExecution(...)\nswitch {\ncase err == nil:\n  // proceed\ncase errors.Is(err, shard.ErrShardStatusUnknown):\n  // shard not yet acquired; retry\ncase shard.IsShardClosed(err):\n  // shard ownership lost; re-lookup shard\n}","typeGuard":"func shardUsable(ctx shard.Context) bool {\n  // probe with a cheap call that maps state to error rather than panicking\n  return ctx.ErrorByState() == nil // via public surface; panics only on corrupt state\n}","tryCatchPattern":"func safeUpdate(ctx shard.Context, req *historyservice.UpdateWorkflowExecutionRequest) (resp interface{}, err error) {\n  defer func() { if r := recover(); r != nil { err = fmt.Errorf(\"shard context invalid state: %v\", r) } }()\n  return ctx.UpdateWorkflowExecution(context.Background(), req)\n}","preventionTips":["Treat ErrShardStatusUnknown and shard-closed errors as retry/redirect signals, not bugs","Keep every switch over contextState exhaustive; update all of them when adding a state","Run shard controller tests under -race to catch unsynchronized state writes"],"tags":["go","shard-controller","state-machine","unhandled-enum-case"],"backgroundTag":"invalid-shard-state","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}