Skip to content

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

Description

@jhrozek

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 (#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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions