jdx/mise · error

cli-spec entry names

Error message

cli-spec entry names {bin:?} in format {format:?}

What it means

When completion_script iterates cli-spec sources, each entry's generated binary name and format (e.g. `fish`, `bash`) must both be plain file names because they are used to build cache/resource paths. If a cli-spec entry yields a bin or format containing path components or traversal characters, mise throws this before running the tool to generate the spec.

Solutions

  1. Fix the cli-spec entry in the tool's packslip manifest so the bin and format placeholders resolve to bare names (`bash`, `fish`).
  2. Check the tool's completion command for arguments emitting path-like values and strip them to file names in the spec template.
  3. Report/upgrade the packslip upstream if the release manifest is wrong.
  4. As a workaround, generate the completion manually with the tool's own completion command.

Example fix

// before
[resources.cli-spec]
command = "mytool completions --out dir/{format}"
// after
[resources.cli-spec]
command = "mytool completions --format {format}"
Defensive patterns

Strategy: validation

Validate before calling

fn is_safe_component(s: &str) -> bool { !s.is_empty() && s != "." && s != ".." && !s.contains('/') && !s.contains('\\') }
assert!(is_safe_component(bin) && is_safe_component(format));

Type guard

fn is_plain_file_name(s: &str) -> bool { !s.is_empty() && !s.contains('/') && !s.contains('\\') && s != "." && s != ".." }

Prevention

When it happens

Trigger: A packslip cli-spec entry whose completion command produces a bin name or format with `/`, `.`, `..` (e.g. the spec command template or `--format` placeholder interpolation yields `sub/dir` or an absolute path).

Common situations: Malformed cli-spec in the packslip manifest (command template inserting directories); upstream changed its completion command output naming; a spec command flag value containing slashes interpolated into bin/format.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at src/packslip.rs:1539

        let attempt = match source {
            CompletionSource::File(path) => file::read_to_string(&path),
            CompletionSource::Spec { format, bin, path } => {
                derive_from_spec(&format, &bin, &path, shell)
            }
            CompletionSource::Exec(argv, env) => run_tool(config, &backend, &tv, &argv, &env).await,
            CompletionSource::SpecExec {
                format,
                bin,
                argv,
                env,
            } => {
                // Any failure here is one more reason to try the next source,
                // not the end of the search. The spec is kept in the install:
                // a script derived from it names the file at completion time,
                // so it has to outlive this command.
                async {
                    if !file::is_plain_file_name(&bin) || !file::is_plain_file_name(&format) {
                        bail!("cli-spec entry names {bin:?} in format {format:?}");
                    }
                    let spec = run_tool(config, &backend, &tv, &argv, &env).await?;
                    // A spec generated for one shell, as `{shell}` in the
                    // command allows, is not the spec for another, and two
                    // shells generating at once must not read each other's
                    // half-written file. `shell` is a plain file name: the
                    // cache path above refuses anything else.
                    let dir = install_path.join(RESOURCES_DIR).join("specs").join(shell);
                    file::create_dir_all(&dir)?;
                    let path = dir.join(format!("{bin}.{format}"));
                    file::write_atomic(&path, &spec)?;
                    derive_from_spec(&format, &bin, &path, shell)
                }
                .await
            }
        };
        match attempt {
            Ok(script) if script.trim().is_empty() => {

View on GitHub (pinned to 533346cc37)