{"record":{"id":"2e6bec03c7e9b6b0","repo":"semaphoreui/semaphore","slug":"runner-is-not-registered","errorCode":null,"errorMessage":"runner is not registered","messagePattern":"runner is not registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/runners/job_pool.go","lineNumber":221,"sourceCode":"\treturn false\n}\n\nfunc (p *JobPool) Register(configFilePath *string) (err error) {\n\n\tok := p.tryRegisterRunner(configFilePath)\n\n\tif !ok {\n\t\terr = fmt.Errorf(\"runner registration failed\")\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc (p *JobPool) Unregister() (err error) {\n\n\tif util.Config.Runner.Token == \"\" {\n\t\treturn fmt.Errorf(\"runner is not registered\")\n\t}\n\n\turl := util.Config.WebHost + \"/api/internal/runners\"\n\n\treq, err := http.NewRequest(\"DELETE\", url, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tlog.WithFields(log.Fields{\n\t\t\"context\": \"unregistration\",\n\t\t\"url\":     url,\n\t}).Debug(\"Sending unregistration request to the server\")\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn\n\t}","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/runners/job_pool.go#L203-L239","documentation":"Unregister was called but the runner has no registration token in config (util.Config.Runner.Token is empty), meaning it was never registered (or the token was removed). Unregistering without a token is meaningless, so it fails immediately without making any HTTP request.","triggerScenarios":"Calling JobPool.Unregister() (via unregisterRunner) when the runner config has no Runner.Token — e.g. unregistering a runner that never completed Register, or after the token was blanked in config.","commonSituations":"Shutdown/cleanup path runs even when startup registration failed; operator runs the unregister command against a fresh runner that was never registered; config file regenerated without the token.","solutions":["Only call Unregister after a successful Register; guard the shutdown path on the runner's registered state.","If the runner should be registered, run Register first to obtain and persist a token.","If unregistering manually, ensure the config file contains the runner's token."],"exampleFix":"// before\npool.Unregister()\n// after\nif util.Config.Runner.Token != \"\" {\n    if err := pool.Unregister(); err != nil { log.Error(err) }\n}","handlingStrategy":"validation","validationCode":"if util.Config.Runner.Token == \"\" {\n    log.Info(\"runner not registered; skipping unregister\")\n    return nil\n}\nreturn pool.Unregister()","typeGuard":"func isRegistered() bool { return util.Config.Runner.Token != \"\" }","tryCatchPattern":"if err := pool.Unregister(); err != nil {\n    if strings.Contains(err.Error(), \"not registered\") {\n        return nil // nothing to do\n    }\n    return err\n}","preventionTips":["Track registration state in the runner lifecycle and gate Unregister on it.","Call Unregister only after a successful Register.","Persist the token so restarts know the runner is registered."],"tags":["runner","unregister","state","token"],"backgroundTag":"invalid-state-transition","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}