jdx/mise · error · eyre::Report

unterminated use block

Error message

unterminated use block

What it means

parse_workfile tracks multi-line `use (` blocks in go.work and requires them to be closed by a line that is exactly ")" before end of file; an unclosed block leaves the parser in block state at EOF and this error aborts Go workspace discovery. Single-line `use ./dir` entries are unaffected.

Source

Thrown at src/task/workspace/go.rs:145

                format!("invalid use directive on line {line_number}")
            })?));
            continue;
        }

        let Some(arguments) = directive_arguments(line, "use") else {
            continue;
        };
        if arguments == "(" {
            in_use_block = true;
        } else {
            directories.push(PathBuf::from(parse_argument(arguments).wrap_err_with(
                || format!("invalid use directive on line {line_number}"),
            )?));
        }
    }

    if in_use_block {
        bail!("unterminated use block");
    }
    Ok(directories)
}

fn read_module_path(context: &WorkspaceDiscoveryContext, path: &Path) -> Result<String> {
    let contents = context
        .read_to_string(path)
        .wrap_err_with(|| format!("failed to read Go module metadata {}", path.display()))?;
    let mut module_path = None;

    for (index, raw_line) in contents.lines().enumerate() {
        let line = strip_comment(raw_line).trim();
        let Some(arguments) = directive_arguments(line, "module") else {
            continue;
        };
        let parsed = parse_argument(arguments)
            .wrap_err_with(|| format!("invalid module directive on line {}", index + 1))?;
        if module_path.replace(parsed).is_some() {

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Add the closing ")" on its own line at the end of the use block
  2. Prefer `go work use ./dir` or `go work edit -use ./dir` so the toolchain keeps the file well-formed
  3. Validate with `go work sync` — the go toolchain itself rejects an unterminated block, catching it before mise runs

Example fix

# before (go.work)
use (
    ./api
    ./worker

# after
use (
    ./api
    ./worker
)
Defensive patterns

Strategy: validation

Validate before calling

fn use_blocks_closed(contents: &str) -> bool {
    let mut depth_open = false;
    for raw in contents.lines() {
        let line = raw.split("//").next().unwrap_or("").trim();
        if line == "use (" { depth_open = true; }
        else if line == ")" && depth_open { depth_open = false; }
    }
    !depth_open
}

Type guard

fn is_balanced_go_work(contents: &str) -> bool {
    let mut in_block = false;
    for raw in contents.lines() {
        let line = raw.split("//").next().unwrap_or("").trim();
        match line.as_str() {
            "use (" => in_block = true,
            ")" if in_block => in_block = false,
            _ => {}
        }
    }
    !in_block
}

Try / catch

Err(report) if report.to_string().contains("unterminated use block") => {
    // append the missing closer and retry once, then fail loudly if it recurs
    contents.push_str("\n)\n");
    retry_parse(&contents)?;
}

Prevention

When it happens

Trigger: A go.work whose `use (` block is missing the closing ")"; a truncated file (last line lost in an edit or failed write); a merge conflict resolution that dropped the closing paren; a commented-out `)` line (// ) is stripped by strip_comment and no longer closes the block).

Common situations: Hand-editing go.work instead of using `go work edit`; git merge conflicts in go.work; copy-pasting a block and missing the tail; tooling that appends entries without preserving the closer.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/a5be3660307a41e6. Report an issue: GitHub.