Skip to content

Toast suppression: prevent flooding error messages while allowing currently muted classes (eg I/O) to be surfaced #771

@HaleTom

Description

@HaleTom

Description

The TUI toast system (packages/opencode/src/cli/cmd/tui/ui/toast.tsx) has no rate limiting or deduplication. Each call to toast.show() immediately replaces the current toast and auto-dismisses after 5 seconds. When multiple errors fire in rapid succession, the user only sees the last one — earlier error messages are invisible because they're immediately overwritten.

This was discovered while investigating #758, where mimo -c hangs silently when no sessions exist. That bug is a missing toast; this issue is the inverse problem: toasts that exist but can't be seen because they're overwritten.

Why this matters beyond sandbox

File IO errors don't just matter in sandbox/jail environments. In Unix, everything is a file — filesystem permission errors, NFS failures, full disks, and read-only mounts all cause cascading IO failures. When the TUI silently swallows .catch(() => {}) on history, frecency, and stash writes, users in these environments get no signal that anything is wrong.

Current behavior

toast.show() in ui/toast.tsx:61-67:

show(options: ToastOptions) {
  const { duration = 5000, ...currentToast } = options
  setStore("currentToast", currentToast)
  if (timeoutHandle) clearTimeout(timeoutHandle)
  timeoutHandle = setTimeout(() => {
    setStore("currentToast", null)
  }, duration).unref()
},
  • Each call clears the previous timeout and replaces the current toast
  • No deduplication: the same error message can flash 20 times in a row
  • No rate limiting: a burst of IO failures produces a rapid strobe of toasts

Proposed behavior

Add a toast suppressor with two controls:

  1. Max warns per message: Once a specific message has been shown N times (e.g., 3), suppress further occurrences for the rest of the session
  2. Minimum interval: Don't show the same message again until at least T seconds (e.g., 30s) have passed since the last showing

These can be combined: "show this message at most 3 times, and never more than once per 30 seconds."

This allows adding .catch() toasts to currently-silent error paths (file IO, permission replies, question replies) without creating a strobing wall of error messages in read-only environments.

Steps to reproduce

  1. Run mimo in a directory where the .mimo/ data directory is on a read-only filesystem
  2. Observe: no error messages are shown (all file IO failures are silently swallowed)
  3. Hypothetically, if .catch(() => toast.show(...)) were added to all file IO writes: toasts would flash rapidly and only the last would be visible

Expected behavior

  • Each distinct error message should be shown to the user at least once
  • Repeated identical errors should be suppressed after a configurable count
  • The same message should not reappear more than once per configurable time interval

Related

Discovered while investigating #758 (TUI hangs on blank screen when mimo -c runs with no sessions). The audit that found the missing toast also found 40+ .catch(() => {}) calls across the TUI — many in user-initiated paths that should show feedback, but adding toasts to all of them requires deduplication first.

Operating System

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions