BoundaryML/baml · error

test selector `{selector}` uses the old `/` hierarchy separa

Error message

test selector `{selector}` uses the old `/` hierarchy separator; use `::` instead (for example `{}`)

What it means

A test selector passed to `baml test` mixes `::` column separators with the legacy `/` hierarchy separator. The CLI only accepts `::` for hierarchy; `/` was the old separator. The check fires only for the recognizable legacy shape — an unrooted selector containing both `/` and `::`, no wildcard, and not a canonical `root...` selector (whose test names may legitimately contain a literal `/`).

Source

Thrown at baml_language/crates/baml_cli/src/test_command.rs:244

            || !self.profile_exclude.is_empty()
            || !self.cli_include.is_empty()
            || !self.cli_exclude.is_empty()
    }
}

fn validate_selectors<'a>(selectors: impl Iterator<Item = &'a String>) -> Result<()> {
    for selector in selectors {
        let is_canonical_root =
            selector == "root" || selector.starts_with("root.") || selector.starts_with("root::");
        // Canonical ids may contain a literal `/` inside a user-provided test
        // name. Only diagnose the recognizable legacy shape: an unrooted
        // two-column selector whose test tail used `/` for further nesting.
        if selector.contains('/')
            && selector.contains("::")
            && !selector.contains('*')
            && !is_canonical_root
        {
            anyhow::bail!(
                "test selector `{selector}` uses the old `/` hierarchy separator; use `::` instead (for example `{}`)",
                selector.replace('/', "::")
            );
        }
    }
    Ok(())
}

/// Bundle of the engine + tokio runtime + cancellation token shared by every
/// test invocation. Kept as a struct rather than separate arguments so the
/// helper functions don't trip `clippy::too_many_arguments`.
struct RunCtx<'a> {
    engine: &'a Arc<BexEngine>,
    rt: &'a tokio::runtime::Runtime,
    cancel: &'a CancellationToken,
    unhandled_spawn_failures: &'a AtomicUsize,
    logs: TestLogLevel,
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Replace every `/` with `::` in the selector — the error itself shows the corrected form.
  2. Update include/exclude patterns in `[test.profiles]` in baml.toml to use `::`.
  3. Update CI scripts and docs that pass legacy selectors to `baml test`.
  4. If the `/` is part of a genuine root-rooted test name, root the selector with `root.`/`root::` so it is accepted.

Example fix

// before
baml test "my_pkg::Group/SubTest"
// after
baml test "my_pkg::Group::SubTest"
Defensive patterns

Strategy: validation

Validate before calling

// shell: reject legacy '/' separators in test selectors before invoking
case "$SELECTOR" in
  *"/"*) [[ "$SELECTOR" == root* ]] || { echo "use :: not / in '$SELECTOR'"; exit 1; } ;;
esac

Try / catch

baml test "$SELECTOR" || { echo "try: ${SELECTOR//\//::}"; exit 1; }

Prevention

When it happens

Trigger: Passing a selector like `pkg::Column/SubTest` to `baml test` (or in profile include/exclude args) where the test-name tail uses `/` for nesting, the selector contains `::`, has no `*`, and doesn't start with `root.`/`root::`.

Common situations: Copy-pasting test IDs from older docs, scripts, or CI configs written before the `::` migration; hand-writing selectors from memory; migrated repos where include/exclude patterns in baml.toml profiles still use `/`.

Understand the failure class

Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/b8fa146659661561. Report an issue: GitHub.