jdx/mise · error · eyre::Report

bootstrap compose project '{name}' oneshot services must als

Error message

bootstrap compose project '{name}' oneshot services must also appear in services

What it means

`oneshot` marks services that should be run to completion (migrations, seeds) rather than kept running; mise launches them from the same service set as the normal `services` selection. `ComposeRequest::from_toml` therefore requires every oneshot entry to appear in `services` when an explicit `services` list is set.

Source

Thrown at src/system/compose.rs:317

        {
            bail!(
                "bootstrap compose project '{name}' down_volumes and down_images require state = \"absent\""
            );
        }
        if config.state != ComposeState::Running && config.renew_anonymous_volumes {
            bail!(
                "bootstrap compose project '{name}' renew_anonymous_volumes requires state = \"running\""
            );
        }
        let explicit_dependencies = config
            .depends_on
            .iter()
            .map(|dependency| parse_dependency(dependency))
            .collect::<Result<Vec<_>>>()?;
        let services = dedupe(config.services);
        let oneshot: IndexSet<String> = dedupe(config.oneshot).into_iter().collect();
        if !services.is_empty() && oneshot.iter().any(|service| !services.contains(service)) {
            bail!(
                "bootstrap compose project '{name}' oneshot services must also appear in services"
            );
        }
        let files = resolve_paths(&config.project_dir, config.files);
        let env_files = resolve_paths(&config.project_dir, config.env_files);
        let path_dependencies = std::iter::once(ResourceId::new(
            "directory",
            config.project_dir.to_string_lossy(),
        ))
        .chain(
            files
                .iter()
                .chain(&env_files)
                .map(|path| ResourceId::new("file", path.to_string_lossy())),
        )
        .collect::<IndexSet<_>>()
        .into_iter()
        .collect();

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Add the missing oneshot service to `services`: `services = ["api", "worker", "migrate"]`.
  2. Or drop it from `oneshot` if it should not be converged by this project.
  3. Cross-check both lists against `docker compose config --services` output for the project.

Example fix

# before
[bootstrap.compose.web]
project_dir = "/srv/web"
services = ["api", "worker"]
oneshot = ["migrate"]

# after
[bootstrap.compose.web]
project_dir = "/srv/web"
services = ["api", "worker", "migrate"]
oneshot = ["migrate"]
Defensive patterns

Strategy: validation

Validate before calling

let services: std::collections::HashSet<&String> = cfg.services.iter().collect();
for one in &cfg.oneshot {
    assert!(services.contains(one), "oneshot service '{one}' missing from services");
}

Prevention

When it happens

Trigger: A compose table sets `services = ["api", "worker"]` and `oneshot = ["migrate"]` — `migrate` is not in `services`, so parsing bails. (When `services` is empty all configured services are implied, and the check is skipped.)

Common situations: Forgetting to add the migration/seed service to the selected services list; renaming a service in the compose file but only updating one of the two lists.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/8093fcb9691811b2. Report an issue: GitHub.