{"record":{"id":"b53aba6d9694a287","repo":"jdx/mise","slug":"uv-workspace-member-pattern-member-must-be-rel","errorCode":null,"errorMessage":"uv workspace member pattern {member:?} must be relative","messagePattern":"uv workspace member pattern (.+?) must be relative","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/workspace/uv.rs","lineNumber":294,"sourceCode":"        })\n        .collect()\n}\n\nfn discover_members(\n    pattern_root: &Path,\n    canonical_root: &Path,\n    members: &[String],\n    excludes: &[Pattern],\n    context: &WorkspaceDiscoveryContext,\n) -> Result<BTreeSet<PathBuf>> {\n    let options = match_options();\n    // Avoid canonical Windows roots here because their verbatim `\\\\?\\` prefix\n    // contains a character that glob interprets as a wildcard.\n    let escaped_root = Pattern::escape(&pattern_root.to_string_lossy());\n    let mut roots = BTreeSet::new();\n    for member in members {\n        if Path::new(member).is_absolute() {\n            bail!(\"uv workspace member pattern {member:?} must be relative\");\n        }\n        let pattern = format!(\"{escaped_root}/{member}\");\n        for candidate in glob::glob_with(&pattern, options)\n            .wrap_err_with(|| format!(\"invalid uv workspace member pattern {member:?}\"))?\n        {\n            let candidate = candidate.wrap_err_with(|| {\n                format!(\"failed to evaluate uv workspace member pattern {member:?}\")\n            })?;\n            if !context.is_dir(&candidate) {\n                continue;\n            }\n            let candidate = context.canonicalize(&candidate).wrap_err_with(|| {\n                format!(\n                    \"failed to resolve uv workspace member {}\",\n                    candidate.display()\n                )\n            })?;\n            let relative = relative_root(canonical_root, &candidate)?;","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/task/workspace/uv.rs#L276-L312","documentation":"uv workspace tool.uv.workspace.members entries are glob patterns that mise expands relative to the workspace root. Absolute paths cannot be joined into the escaped-root glob pattern, so mise rejects them up front rather than producing a broken pattern. uv itself also documents members as root-relative globs.","triggerScenarios":"discover_members processes a members array entry like \"/abs/path/pkg\" or \"C:\\\\pkg\" where Path::is_absolute() is true.","commonSituations":"Copy-pasting an absolute path from `pwd` output into pyproject.toml; IDE auto-complete inserting full paths; generated configs from scripts that emitted absolute paths.","solutions":["Change the member entry to a path relative to the workspace root, e.g. \"packages/*\".","If the package lives outside the workspace directory, move it inside or use a symlink within the root.","Use glob patterns like \"members/*\" or \".\" for the root project itself."],"exampleFix":"# before\n[tool.uv.workspace]\nmembers = [\"/home/me/proj/packages/*\"]\n# after\n[tool.uv.workspace]\nmembers = [\"packages/*\"]","handlingStrategy":"validation","validationCode":"const members = doc['tool']?.['uv']?.['workspace']?.['members'] ?? [];\nfor (const m of members) {\n  if (require('path').isAbsolute(m)) throw new Error(`member ${m} must be relative`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always express uv workspace members relative to the workspace root.","Use globs (packages/*) instead of enumerating absolute paths."],"tags":["uv","python","workspace","glob"],"backgroundTag":"invalid-config-value","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}