semaphoreui/semaphore · error
executor provider is not initialised (check runner executor…
Error message
executor provider is not initialised (check runner executor config)
What it means
newExecutor was invoked with a nil ExecutorProvider, so no executor can be constructed for the job. The provider is created from the runner executor config; nil means the factory never produced one (e.g. unknown executor type or config omitted), so the guard fails fast with an actionable message instead of panicking on a nil pointer.
Solutions
- Check runner startup logs for an error from newExecutorProvider and fix the executor configuration.
- Set a valid executor.type in the runner config and restart the runner.
- Ensure the runner fails fast at startup (or re-initializes the provider) instead of reaching the job loop with a nil provider.
Example fix
// before
provider, _ := newExecutorProvider(cfg) // error ignored, provider nil
// after
provider, err := newExecutorProvider(cfg)
if err != nil {
return fmt.Errorf("init executor provider: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
if pool.Provider() == nil {
return errors.New("executor provider not initialized; check runner executor config before accepting jobs")
} Type guard
func providerReady(p tasks.ExecutorProvider) bool { return p != nil } Try / catch
ex, err := newExecutor(jobData, keys, provider)
if err != nil {
if strings.Contains(err.Error(), "not initialised") {
log.Error("executor provider missing; halting job processing for reconfiguration")
return
}
return err
} Prevention
- Fail runner startup hard when the executor provider cannot be built.
- Log provider construction errors prominently at boot.
- Test the runner config with a dry-run startup before production.
When it happens
Trigger: checkNewJobs picks up a job and calls newExecutor while the JobPool's provider is nil — typically because newExecutorProvider returned (nil, err) at startup (unknown executor type, docker provider init failure) or the config tree was nil and nothing defaulted.
Common situations: Runner started with a misconfigured/invalid executor section so the provider was never initialized; provider construction failed at startup but the job loop kept running.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- unknown runner executor type
- Empty token
- runner registration failed
- runner is not registered
- encountered error while unregistering runner; server…
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/a1543340bbaa78b4.
Report an issue: GitHub.
Appendix: source
Thrown at services/runners/executor_factory.go:61
// runner code free of nil-checks against the config tree.
func resolveExecutorType(executorCfg *util.ExecutorConfig) util.ExecutorType {
if executorCfg == nil || executorCfg.Type == "" {
return util.ExecutorTypeLocal
}
return executorCfg.Type
}
// newExecutor wires per-task data through the Provider. Access keys are hydrated
// here (not inside each Provider) so the behaviour is identical regardless of
// strategy: ansible vault passwords, SSH keys, inventory keys, and the inventory
// repo SSH key all land on the JobData before the Executor is built.
func newExecutor(
jobData JobData,
accessKeys map[int]db.AccessKey,
provider tasks.ExecutorProvider,
) (tasks.Executor, error) {
if provider == nil {
return nil, fmt.Errorf("executor provider is not initialised (check runner executor config)")
}
hydrateJobAccessKeys(&jobData, accessKeys)
return provider.NewExecutor(
jobData.Task,
jobData.Template,
jobData.Inventory,
jobData.Repository,
jobData.Environment,
jobData.JWT,
)
}
// hydrateJobAccessKeys decrypts/wires the access keys the server sent us into the
// per-task data. Lives in the factory (not the Provider) so the behaviour is
// identical across strategies — every executor sees the same shape of JobData.
func hydrateJobAccessKeys(jobData *JobData, accessKeys map[int]db.AccessKey) {View on GitHub (pinned to 1774ccb71a)