jdx/mise · error

uv project at has an empty project.name

Error message

uv project at {} has an empty project.name

What it means

When discovering uv (Python) workspaces, mise reads pyproject.toml [project] tables and requires a non-empty project.name because the name becomes the workspace member's ProjectId. A [project] table with name = "" is invalid per uv/packaging rules, so mise refuses the project instead of producing a colliding or empty identifier.

Solutions

  1. Set a valid project name in pyproject.toml: name = "my-project".
  2. If the file is only a virtual/lock workspace config without a real project, remove the empty [project] table so it isn't treated as a project.
  3. Re-run your project scaffolder to fill in the name placeholder.

Example fix

# before
[project]
name = ""
# after
[project]
name = "my-project"
Defensive patterns

Strategy: validation

Validate before calling

import TOML from '@iarna/toml';
const doc = TOML.parse(readFileSync('pyproject.toml', 'utf8'));
if (doc.project && (!doc.project.name || doc.project.name === '')) {
  throw new Error('pyproject.toml [project] has empty name');
}

Prevention

When it happens

Trigger: discover_with_context reads a pyproject.toml where [project] exists but project.name is an empty string.

Common situations: Scaffolded pyproject.toml with a placeholder empty name; a template that failed to substitute the project name; someone deleted the name value while editing.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        let mut known_roots = workspace_roots.clone();
        let mut queue = workspace_roots.iter().cloned().collect::<VecDeque<_>>();
        let mut manifests = BTreeMap::new();
        while let Some(project_root) = queue.pop_front() {
            if manifests.contains_key(&project_root) {
                continue;
            }
            let relative = relative_root(&canonical_root, &project_root)?;
            let pyproject_path = project_root.join(PYPROJECT_TOML);
            let pyproject = if project_root == canonical_root {
                root_pyproject.clone()
            } else {
                read_pyproject(context, &pyproject_path)?
            };
            let project = pyproject.project.as_ref().ok_or_else(|| {
                eyre::eyre!("uv project at {} is missing [project]", relative.display())
            })?;
            if project.name.is_empty() {
                bail!(
                    "uv project at {} has an empty project.name",
                    relative.display()
                );
            }
            let id = ProjectId::new(self.id(), &normalize_package_name(&project.name))?;
            let workspace_member = workspace_roots.contains(&project_root);

            for dependency in dependency_names(&pyproject) {
                let Some((source, source_base)) = effective_source(
                    &pyproject,
                    &root_pyproject,
                    &dependency,
                    workspace_member,
                    &project_root,
                    &canonical_root,
                ) else {
                    continue;
                };

View on GitHub (pinned to 533346cc37)