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
- Add the closing ")" on its own line at the end of the use block
- Prefer `go work use ./dir` or `go work edit -use ./dir` so the toolchain keeps the file well-formed
- 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
- Edit go.work through `go work use` / `go work edit` instead of by hand
- Check that a lone ")" closer survived merge-conflict resolution (a commented `// )` no longer closes the block)
- Run `go work sync` before committing go.work changes
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
- directive requires one argument
- unterminated interpreted Go string
- unterminated raw Go string
- unexpected raw Go string delimiter
- directive requires exactly one argument
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/a5be3660307a41e6.
Report an issue: GitHub.