jdx/mise · error

uv workspace member at

Error message

uv workspace member at {} is missing {PYPROJECT_TOML}

What it means

After expanding a workspace member glob and excluding excluded paths, mise requires each matched candidate directory to contain a pyproject.toml (PYPROJECT_TOML) — a uv workspace member is defined as a Python project directory. A matched directory without one cannot be a member, so discovery bails with the member's path relative to the root.

Solutions

  1. Add a pyproject.toml to the matched directory if it is meant to be a member.
  2. Narrow the members glob so it excludes non-project directories (e.g. "packages/pkg-*").
  3. Add the directory's name to tool.uv.workspace.exclude.
  4. Move non-project folders outside the globbed directory.

Example fix

# before
[tool.uv.workspace]
members = ["packages/*"]  # packages/docs has no pyproject.toml
# after
[tool.uv.workspace]
members = ["packages/*"]
exclude = ["packages/docs"]
Defensive patterns

Strategy: validation

Validate before calling

const glob = require('glob');
for (const dir of glob.sync('packages/*')) {
  if (!existsSync(join(dir, 'pyproject.toml'))) {
    console.warn(`${dir} matches members glob but has no pyproject.toml`);
  }
}

Prevention

When it happens

Trigger: A members glob (e.g. "packages/*") matches a plain directory that has no pyproject.toml — e.g. an assets folder or docs folder under packages/.

Common situations: Dropping a non-Python directory into a globbed members path; deleting pyproject.toml from a member while keeping it in the workspace; overly broad member patterns like "*".

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/9286d16c0a3f348a. Report an issue: GitHub.

Appendix: source

Thrown at src/task/workspace/uv.rs:317

        {
            let candidate = candidate.wrap_err_with(|| {
                format!("failed to evaluate uv workspace member pattern {member:?}")
            })?;
            if !context.is_dir(&candidate) {
                continue;
            }
            let candidate = context.canonicalize(&candidate).wrap_err_with(|| {
                format!(
                    "failed to resolve uv workspace member {}",
                    candidate.display()
                )
            })?;
            let relative = relative_root(canonical_root, &candidate)?;
            if is_excluded(&relative, excludes) {
                continue;
            }
            if !context.is_file(&candidate.join(PYPROJECT_TOML)) {
                bail!(
                    "uv workspace member at {} is missing {PYPROJECT_TOML}",
                    relative.display()
                );
            }
            roots.insert(candidate);
        }
    }
    Ok(roots)
}

fn match_options() -> MatchOptions {
    MatchOptions {
        case_sensitive: true,
        require_literal_separator: true,
        require_literal_leading_dot: true,
    }
}

View on GitHub (pinned to 533346cc37)