semaphoreui/semaphore · error

unknown runner executor type

Error message

unknown runner executor type %q

What it means

The runner's executor factory did not recognize the configured executor type. newExecutorProvider switches on the executor type resolved from the runner config (defaulting to "local" when empty) and the default branch means the value is neither local, docker, nor any supported provider. No executor provider is created and the runner cannot execute jobs.

Solutions

  1. Open the runner config and set executor.type to a supported value (e.g. "local" or "docker").
  2. Check for typos and trailing whitespace in the executor type value.
  3. Confirm the binary version supports the configured executor type; upgrade or downgrade accordingly.
  4. Remove the executor section entirely to fall back to the "local" default.

Example fix

// before (config)
executor:
  type: dockr
// after
executor:
  type: docker
Defensive patterns

Strategy: validation

Validate before calling

validTypes := map[string]bool{"local": true, "docker": true}
execType := cfg.Executor.Type
if execType == "" { execType = "local" }
if !validTypes[execType] {
    return fmt.Errorf("executor type %q not supported by this runner build", execType)
}

Try / catch

pool, err := runners.NewJobPool(cfg)
if err != nil {
    if strings.Contains(err.Error(), "unknown runner executor type") {
        log.Fatalf("fix executor.type in runner config: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: newExecutorProvider is called with a config whose executor.type (after resolveExecutorType defaulting) is an unrecognized string, e.g. a typo like "dockr" or a type not compiled into this build.

Common situations: Typo in the runner's config.yml executor type; copying config from documentation of a newer/older version with extra executor types; environment-specific config templates with invalid values.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/01cb25fd1cddf126. Report an issue: GitHub.

Appendix: source

Thrown at services/runners/executor_factory.go:37

// else (JobPool, executor lifecycle, access-key hydration) needs to change.
func newExecutorProvider(executorCfg *util.ExecutorConfig, keyInstaller db_lib.AccessKeyInstaller) (tasks.ExecutorProvider, error) {
	switch resolveExecutorType(executorCfg) {
	case util.ExecutorTypeLocal:
		return tasks.NewLocalExecutorProvider(keyInstaller), nil
	case util.ExecutorTypeKubernetes:
		k8sCfg := util.RunnerK8sConfig{}
		if executorCfg != nil {
			k8sCfg = executorCfg.K8s
		}
		return k8s.NewProvider(k8sCfg)
	case util.ExecutorTypeDocker:
		dockerCfg := util.RunnerDockerConfig{}
		if executorCfg != nil {
			dockerCfg = executorCfg.Docker
		}
		return docker.NewProvider(dockerCfg)
	default:
		return nil, fmt.Errorf("unknown runner executor type %q", resolveExecutorType(executorCfg))
	}
}

// resolveExecutorType returns the executor type from config, defaulting to "local"
// when the field is missing or empty. Defaulting in one place keeps the rest of the
// 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(

View on GitHub (pinned to 1774ccb71a)