temporalio/temporal · error
invalid state
Error message
invalid state
What it means
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.
Source
Thrown at service/history/shard/context_impl.go:1142
}
func (s *ContextImpl) getRangeIDLocked() int64 {
return s.shardInfo.GetRangeId()
}
func (s *ContextImpl) errorByState() error {
s.stateLock.Lock()
defer s.stateLock.Unlock()
switch s.state {
case contextStateInitialized, contextStateAcquiring:
return ErrShardStatusUnknown
case contextStateAcquired:
return nil
case contextStateStopping, contextStateStopped:
return s.newShardClosedErrorWithShardID()
default:
panic("invalid state")
}
}
func (s *ContextImpl) errorByNamespaceStateLocked(
namespaceName namespace.Name,
workflowID string,
) error {
if s.handoverTracker.IsInHandover(namespaceName, workflowID) {
return consts.ErrNamespaceHandover
}
return nil
}
func (s *ContextImpl) generateTaskIDLocked() (int64, error) {
taskKey, err := s.taskKeyManager.generateTaskKey(tasks.CategoryTransfer)
if err != nil {
return -1, err
}View on GitHub (pinned to bde624efd1)
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
Example fix
// before
case contextStateStopping, contextStateStopped:
return s.newShardClosedErrorWithShardID()
default:
panic("invalid state")
// after
case contextStateStopping, contextStateStopped:
return s.newShardClosedErrorWithShardID()
case contextStateLoading: // newly added state
return ErrShardStatusUnknown
default:
panic("invalid state") Defensive patterns
Strategy: validation
Validate before calling
// On shard-context errors, check the returned sentinel errors instead of panics:
err := ctx.UpdateWorkflowExecution(...)
switch {
case err == nil:
// proceed
case errors.Is(err, shard.ErrShardStatusUnknown):
// shard not yet acquired; retry
case shard.IsShardClosed(err):
// shard ownership lost; re-lookup shard
} Type guard
func shardUsable(ctx shard.Context) bool {
// probe with a cheap call that maps state to error rather than panicking
return ctx.ErrorByState() == nil // via public surface; panics only on corrupt state
} Try / catch
func safeUpdate(ctx shard.Context, req *historyservice.UpdateWorkflowExecutionRequest) (resp interface{}, err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("shard context invalid state: %v", r) } }()
return ctx.UpdateWorkflowExecution(context.Background(), req)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ErrInvalidTransition
- ControllerImpl for host '%v' shutting down
- task has no deadline or destination
- upload stream unknown status: %v
- ExecutableTaskTracker encountered unknown task state: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/d20b2f1ef7852805.
Report an issue: GitHub.