Skip to content

[Bug]: exit notification delivery failures are silently swallowed — agent never wakes, no diagnostic #54

Open

Description

@helixzz

Summary

NotificationManager.sendExitNotification() wraps the promptAsync call in a bare catch {} that discards the error. When delivery fails, the <pty_exited> block still shows up in the conversation but no assistant turn is started — the agent appears to simply ignore the notification, and the user has to manually send a message ("continue") to get it to react.

There is no user-visible signal and no plugin-side log, so from the outside this is indistinguishable from "the model decided not to respond". It took a fair amount of digging through the OpenCode server log to find the real cause.

Environment

  • opencode-pty 0.3.6 (current npm latest)
  • OpenCode 1.18.11
  • Linux x86_64, Bun runtime
  • notifyOnExit: true

What happens

  1. Spawn a session with notifyOnExit: true; it exits normally (exit code 0).
  2. The <pty_exited> block appears in the conversation as a user-role message:
<pty_exited>
ID: pty_e1073517
Description: Bootstrap conda base with separate root prefix
Exit Code: 0
TimeoutSeconds: 2400
Timed Out: no
Output Lines: 10
Last Line: Python 3.12.13
</pty_exited>

Use pty_read to check the full output.
  1. No assistant turn follows. The agent stays idle until the user sends something.
  2. Nothing is logged by the plugin. The only trace is in the OpenCode server log:
level=ERROR message="prompt_async failed" sessionID=ses_...
cause="Cause([Die(ProviderModelNotFoundError: Model not found: modelverse/claude-opus-4-7.)])"

63 occurrences of that exact failure in my log.

Root cause in my case

The trigger was my own stale configuration: the top-level model in opencode.json pointed at a model that no longer existed (I had moved those models to a different provider id and forgot to update the default).

Because sendExitNotification() on 0.3.6 omits the model, OpenCode resolves it as input.model ?? agent.model ?? currentModel(sessionID) and ended up on that dangling global default → ProviderModelNotFoundError → the prompt dies → no run.

So the misconfiguration was mine. The bug being reported here is that the failure is completely invisible, which turned a one-line config typo into a confusing "the agent ignores PTY notifications" symptom.

Note on #50

#50 ("fix: preserve model for exit notifications", merged 2026-07-21) addresses exactly this class of model-resolution failure by passing the parent session's model explicitly, and main now has it.

Two things remain, though:

  1. It is not released. The newest npm version / GitHub release is still v0.3.6 (2026-07-05), which predates the merge. Anyone installing via the documented "plugin": ["opencode-pty"] path gets a build without the fix. A patch release would help.
  2. The silent catch is still on main (src/plugin/pty/notification-manager.ts, the outer catch {} around promptAsync). fix: preserve model for exit notifications #50 removes one cause of delivery failure, but any other failure — auth, server restart mid-run, session gone, permission, transport — would still be swallowed with no trace.

Suggested fix

At minimum, log it:

} catch (error) {
  console.error(
    `[opencode-pty] failed to deliver exit notification for ${session.id}:`,
    error instanceof Error ? error.message : String(error),
  )
}

Optionally, since a failed delivery means the agent will never be woken, it may be worth surfacing it where the user can actually see it — e.g. including the delivery failure in the next pty_read / pty_list output, so the state isn't silently lost.

The inner catch {} around the model lookup on main looks intentional (documented fallback for older servers) and seems fine to leave as-is.

Repro

Any promptAsync failure reproduces it. The cheapest way:

  1. Set the top-level model in opencode.json to a non-existent id, e.g. "model": "someprovider/does-not-exist".
  2. pty_spawn with notifyOnExit: true running something short, e.g. bash -c "echo hi".
  3. On exit: the <pty_exited> block appears, no assistant turn starts, and the plugin logs nothing.

Thanks for the plugin — the PTY tools themselves have been working well; this was purely an observability gap.

Activity

  1. CryptoAlchemik commented on Nov 5, 2026

    @CryptoAlchemik

    Reproduced silent loss of <pty_exited> in the v2 host (opencode 2.1.4, @panpan2026/opencode-pty 0.5.0 via a local v2 adapter; upstream main b6c2067).

    Findings:

    1. The adapter imports manager but never calls manager.init(client). NotificationManager.client stays null, so sendExitNotification returns without sending and without logging.
    2. v1 promptAsync (POST /session/:id/prompt_async) returns 405 on the v2 server even with auth. The v2 route POST /api/session/:id/prompt?directory=… returns 200.
    3. Upstream main (V2SessionNotifier, fix(v2): register pty tools, slash commands and exit notifications via the V2 plugin SDK #61) sends id: pty_<id>_exited. Server 2.1.4 rejects it with 400 Expected a string starting with "msg_", so the notification is never delivered (no row in session_message). The failure is logged via console.error, with no retry and no undelivered marker.
    4. Working call (isolated server, example session): omit id, use delivery: "queue" → 200, <pty_exited> appears in session_message.
    5. Missing parent session → 404, logged; no retry.

    Suggested fixes:

    • Do not send a custom id (or use a msg_-prefixed one).
    • Call manager.init(client) / setNotifier in the host adapter.
    • Retry failed deliveries and write an undelivered marker, so failures are not silent.

    Versions: opencode 2.1.4, upstream main b6c2067, @panpan2026/opencode-pty 0.5.0.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions