{"record":{"id":"c407cedd892c1fe6","repo":"hashicorp/nomad","slug":"unexpected-task-coordinator-state-d","errorCode":null,"errorMessage":"Unexpected task coordinator state %d","messagePattern":"Unexpected task coordinator state (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/tasklifecycle/coordinator.go","lineNumber":41,"sourceCode":"\tcoordinatorStatePoststop\n)\n\nfunc (s coordinatorState) String() string {\n\tswitch s {\n\tcase coordinatorStateInit:\n\t\treturn \"init\"\n\tcase coordinatorStatePrestart:\n\t\treturn \"prestart\"\n\tcase coordinatorStateMain:\n\t\treturn \"main\"\n\tcase coordinatorStatePoststart:\n\t\treturn \"poststart\"\n\tcase coordinatorStateWaitAlloc:\n\t\treturn \"wait_alloc\"\n\tcase coordinatorStatePoststop:\n\t\treturn \"poststart\"\n\t}\n\tpanic(fmt.Sprintf(\"Unexpected task coordinator state %d\", s))\n}\n\n// lifecycleStage represents a lifecycle configuration used for task\n// coordination.\n//\n// Not all possible combinations of hook X sidecar are defined, only the ones\n// that are relevant for coordinating task initialization order. For example, a\n// main task with sidecar set to `true` starts at the same time as a\n// non-sidecar main task, so there is no need to treat them differently.\ntype lifecycleStage uint8\n\nconst (\n\t// lifecycleStagePrestartEphemeral are tasks with the \"prestart\" hook and\n\t// sidecar set to \"false\".\n\tlifecycleStagePrestartEphemeral lifecycleStage = iota\n\n\t// lifecycleStagePrestartSidecar are tasks with the \"prestart\" hook and\n\t// sidecar set to \"true\".","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/tasklifecycle/coordinator.go#L23-L59","documentation":"The String() method for coordinatorState translates the state enum to a human-readable name and panics when handed a value outside the known cases. This means the coordinator state variable was corrupted, a new state was added without updating String(), or an uninitialized/invalid state value flowed into logging or hook-label code.","triggerScenarios":"Calling String() (directly or via %s/%v formatting in logs and hook labels) with a coordinatorState value not covered by the switch — e.g., a zero-value/uninitialized coordinator, or a newly added coordinatorState constant missing a case after a library upgrade.","commonSituations":"Upgrading the library to a version that introduced a new coordinator state while custom code switches on state values; constructing a coordinator struct manually leaving the state field zero; custom logging that casts ints to coordinatorState.","solutions":["Update to a library version where String() covers all coordinatorState values","If you added a new state constant, add the corresponding case to String()","Ensure coordinators are created via their constructor so state is initialized properly","Avoid casting raw ints to coordinatorState; keep state assignment inside the coordinator"],"exampleFix":"// before\npanic(fmt.Sprintf(\"Unexpected task coordinator state %d\", s))\n// after\ndefault:\n\treturn fmt.Sprintf(\"unknown(%d)\", s) // or return error instead of panicking","handlingStrategy":"type-guard","validationCode":"// Validate the state before formatting it\nfunc validCoordinatorState(s coordinatorState) bool {\n\tswitch s {\n\tcase coordinatorStatePrestart, coordinatorStatePoststart,\n\t\tcoordinatorStateWaitAlloc, coordinatorStatePoststop:\n\t\treturn true\n\t}\n\treturn false\n}","typeGuard":"func isKnownCoordinatorState(s coordinatorState) bool {\n\tswitch s {\n\tcase coordinatorStatePrestart, coordinatorStatePoststart,\n\t\tcoordinatorStateWaitAlloc, coordinatorStatePoststop:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}","tryCatchPattern":"// Go: recover when rendering state for logs\nfunc stateLabel(s coordinatorState) (str string, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"bad coordinator state: %v\", r)\n\t\t}\n\t}()\n\treturn s.String(), nil\n}","preventionTips":["Always construct coordinators via their constructor so state is initialized","Never cast raw ints to coordinatorState","After adding a new state constant, update String() and all switches in the same change","After library upgrades, run tests that exercise all lifecycle stages to surface missing-state cases"],"tags":["panic","state-machine","enum","tasklifecycle"],"backgroundTag":"unexpected-enum-state","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}