stacklok / stacklok/mecatl

mecatui: shared diagnostics log silently discards output when another instance holds the lock

Open
#1,696 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
152
Forks
16
Avg merge
14h 48m
Merged PRs (30d)
536

Description

Summary

The shared per-user diagnostics log ($XDG_STATE_HOME/mecatl/mecatui.log) is
single-writer, and when a second concurrently-running mecatui process can't
acquire the lock, its entire diagnostics stream is silently discarded — no
error, no fallback file, no indication anywhere that logging is a no-op for
that instance.

Where

cmd/mecatui/diaglog.go, openDiagLogWriterWithRetainer:

lock := flock.New(path+".lock",
    flock.SetFlag(os.O_CREATE|os.O_RDWR|syscall.O_NOFOLLOW),
    flock.SetPermissions(0o600),
)
locked, err := lock.TryLock()
if err != nil || !locked {
    _ = lock.Close()
    return io.Discard, noop, false
}

TryLock() is non-blocking. If another mecatui already holds
mecatui.log.lock, this call returns io.Discard as the writer — silently.
There's no retry, no fallback to a sibling/PID-suffixed file, and nothing
surfaced to the user or written anywhere that logging was disabled for this
run.

Reproduction

Confirmed empirically: with several mecatui instances already running
against the same workspace, I launched a new one (PID 52704) and, after it
had fully started, checked lsof -p 52704. It had opened zero file
handles matching mecatui.log or mecatui.log.lock — consistent with
TryLock losing the race and falling back to io.Discard without ever
attempting to open the file.

Why this matters

It's easy to accumulate several mecatui processes over time (leaving
sessions open across projects/terminals, forgetting to exit one before
starting another) — in the case that led to filing this, there were 13
concurrent mecatui processes against the same workspace, some running for
multiple days. Only whichever one won the lock race writes to the shared
log; every other instance's diagnostics — including the one you're actually
trying to debug — vanish with no indication that anything was suppressed.

This directly cost real debugging time: while investigating a separate
startup-latency issue (stacklok/mecatl#1695), the shared log
appeared to have "nothing" for the slow run, which looked like evidence
against several otherwise-correct hypotheses. The actual cause only became
visible once we passed --diagnostics-log <private path> to bypass the
shared, contended log entirely.

Suggested direction (not prescriptive)

When the shared log's lock can't be acquired, fall back to a PID-suffixed
sibling file (e.g. mecatui.<pid>.log) instead of io.Discard, so no
instance's diagnostics are silently lost — worst case, an operator now has to
check more than one file, which is strictly better than checking one file
that might be empty for reasons unrelated to the run itself.


🤖 Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in cmd/mecatui/diaglog.go at openDiagLogWriterWithRetainer, reading how the lock and writer are created when TryLock fails. Reproduce the behavior with concurrent mecatui instances and inspect the existing diagnostics-log path handling. Done means a contending instance retains its diagnostics in a distinct fallback log instead of silently returning io.Discard.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
cli, observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.