jdx/mise · error · eyre::Report

expected identifier after dot

Error message

expected identifier after dot

What it means

A command_wrapper artifact's first array element must be a bare command name: Path::file_name() of the name must equal the whole string and it must not be '.' or '..'. Any '/', trailing separator, or dot-only name is rejected because the name becomes both the staged script name and the symlinked command in bin.

Source

Thrown at crates/aqua-registry/src/template.rs:312

            tokens.next();
            Ok(Expr::Literal(s.to_string()))
        }
        _ => Err(eyre!("expected argument")),
    }
}

fn parse_property_chain(
    tokens: &mut std::iter::Peekable<std::slice::Iter<Token>>,
    mut expr: Expr,
) -> Result<Expr> {
    while matches!(tokens.peek(), Some(Token::Dot)) {
        tokens.next(); // consume dot
        skip_whitespace(tokens);

        if let Some(Token::Ident(prop)) = tokens.next() {
            expr = Expr::PropertyAccess(Box::new(expr), prop.to_string());
        } else {
            bail!("expected identifier after dot");
        }
    }

    Ok(expr)
}

fn skip_whitespace(tokens: &mut std::iter::Peekable<std::slice::Iter<Token>>) {
    while matches!(tokens.peek(), Some(Token::Whitespace(_))) {
        tokens.next();
    }
}

/// Function signature for template functions that return Value trait objects
type TemplateFn = fn(&[Box<dyn Value>]) -> Result<Box<dyn Value>>;

/// Static registry of available template functions
static FUNCTION_REGISTRY: LazyLock<HashMap<&'static str, TemplateFn>> = LazyLock::new(|| {
    let mut registry: HashMap<&'static str, TemplateFn> = HashMap::new();

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Use a plain command name with no path separators ('tool', not 'bin/tool')
  2. If the executable lives in a subdirectory, put the path in the 'executable' option and keep 'name' bare

Example fix

# command_wrapper artifact
# before
{"command_wrapper": ["bin/my-tool", {"executable": "pkg/bin/tool"}]}
# after
{"command_wrapper": ["my-tool", {"executable": "pkg/bin/tool"}]}
Defensive patterns

Strategy: validation

Validate before calling

let p = Path::new(name);
if p.file_name().and_then(|n| n.to_str()) != Some(name) || matches!(name, "." | "..") {
    // reject before parse/install
}

Type guard

fn is_bare_command_name(name: &str) -> bool {
    Path::new(name).file_name().and_then(|n| n.to_str()) == Some(name)
        && !matches!(name, "." | "..")
}

Prevention

When it happens

Trigger: command_wrapper names like 'bin/tool', './tool', 'tool/', 'sub/dir/tool', '.', or '..' in the cask/mise metadata.

Common situations: Authors pasting a relative path into the wrapper name; metadata generators filling the field with a file path instead of a command name.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/aa135900e5984f33. Report an issue: GitHub.