jdx/mise · error · eyre::Report

[monorepo].config_roots is required for monorepo operations

Error message

[monorepo].config_roots is required for monorepo operations

What it means

Monorepo operations (expanding config roots, building the union toolset) require the config file that declares `monorepo_root = true` to also define a non-empty `[monorepo].config_roots` glob list. This error fires when that table is missing entirely or its config_roots array is empty.

Source

Thrown at src/config/mod.rs:783

    ///
    /// `None` matches any existing directory and is used for lockfile migration
    /// and routing. `Some(filenames)` requires a recognized config or idiomatic
    /// version file and is used for full monorepo union/task loading.
    pub(crate) fn monorepo_config_root_dirs(
        &self,
        filenames: Option<&[String]>,
    ) -> Result<Vec<PathBuf>> {
        let monorepo_config = find_monorepo_config(&self.config_files)
            .ok_or_else(|| eyre!("no config file in scope sets monorepo_root = true"))?;
        let monorepo_root = monorepo_config
            .project_root()
            .ok_or_else(|| eyre!("monorepo root config has no project root"))?;
        let patterns = &monorepo_config
            .monorepo()
            .ok_or_else(|| eyre!("[monorepo].config_roots is required for monorepo operations"))?
            .config_roots;
        if patterns.is_empty() {
            bail!("[monorepo].config_roots is required for monorepo operations");
        }
        let roots = match filenames {
            Some(filenames) => {
                expand_config_roots_with_filenames(&monorepo_root, patterns, None, filenames)?
            }
            None => expand_config_root_dirs(&monorepo_root, patterns, None)?,
        };
        if roots.is_empty() {
            bail!("[monorepo].config_roots did not match any config roots");
        }
        Ok(roots)
    }

    pub(crate) async fn monorepo_union_tool_request_set(
        self: &Arc<Self>,
    ) -> Result<ToolRequestSet> {
        Ok(self.monorepo_union().await?.tool_request_set)
    }

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Add a `[monorepo]` table with non-empty globs relative to the monorepo root: `config_roots = ["apps/*", "packages/*"]`
  2. Check the key location: config_roots belongs inside `[monorepo]`, not as a top-level key
  3. If monorepo behavior was unintended, remove `monorepo_root = true` from the config

Example fix

# before
monorepo_root = true

[tools]
node = "22"

# after
monorepo_root = true

[monorepo]
config_roots = ["apps/*", "packages/*"]

[tools]
node = "22"
Defensive patterns

Strategy: validation

Validate before calling

grep -q 'monorepo_root = true' mise.toml && \
  awk '/^\[monorepo\]/{f=1} f && /^config_roots/{if ($0 ~ /\[\s*\]/) exit 1; found=1} END{exit found?0:1}' mise.toml \
  && echo 'config_roots present' || echo 'missing/empty [monorepo].config_roots'

Prevention

When it happens

Trigger: A mise.toml contains `monorepo_root = true` but no `[monorepo]` section, or `[monorepo]` with `config_roots = []`, and any command that needs the monorepo project graph or config roots runs (monorepo task loading, monorepo union).

Common situations: Copy-pasting a monorepo example that only sets monorepo_root; naming the table differently (e.g. `[workspace]`) so mise ignores it; assuming mise auto-detects subprojects without patterns.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/192c815295e025d2. Report an issue: GitHub.