Skip to content

Windows: background service spawns a visible console every ~2s and steals keyboard focus #62988

Closed as duplicate

Description

@nathwn12

Description

On Windows, the background service (opencode serve --service) shells out to opencode service status roughly twice a second. The service is started detached, so it owns no console; every one of those cmd.exe children is therefore given a brand-new console window. A newly created console is allowed to take the foreground, so the window steals keyboard focus about every two seconds and interrupts typing in any other application.

A process-start trace shows the exact chain:

cmd.exe  /d /s /c "opencode service status"
  parent: opencode.exe serve --service
  parent: opencode.exe            (TUI)
  parent: opencode.exe            (launcher)

Cadence and cost, measured over 45 seconds: 24 cmd.exe starts (one every ~1.9 s). Each start was followed by conhost.exe, OpenConsole.exe and two further opencode.exe processes, so roughly five process creations per probe, ~160 per minute.

Notes that narrowed it down:

  • It is not a failure loop. opencode service status returns a healthy URL while this is happening; it is a heartbeat.
  • It is not a plugin. The process that runs the command is the core daemon, opencode serve --service. Third-party TUI plugins were loaded during the capture, but the parent chain is the core service, not a plugin process.
  • It is not the TUI. Killing the TUI does not stop it, and opencode service stop is undone within seconds because the TUI starts a replacement daemon immediately.

Impact: on Windows this makes OpenCode actively hostile to use alongside any other application, because it takes focus from whatever the user is typing into.

Plugins

[email protected], [email protected] (TUI-only). The polling process is the core service, not either plugin.

OpenCode version

opencode v2.2.6, Windows build, bun global install (@opencode/cli).

Steps to reproduce

  1. Use Windows 11 and install opencode 2.2.6 (bun global install; also seen from a scoop-installed copy).
  2. Launch the TUI without --standalone, so it uses the background service, from any launcher that does not give the service an inherited console.
  3. Focus another application with a text field and type continuously.
  4. Every ~2 seconds a console window appears and focus is taken away from the text field.
  5. Observe the process tree for a few seconds and confirm the spawn:
    • child: cmd.exe /d /s /c "opencode service status"
    • parent: opencode.exe serve --service
  6. Optional: count the spawns. A WMI Win32_ProcessStartTrace subscription for 45 seconds gives ~24 cmd.exe starts, each followed by a new conhost.exe.

Expected

No console window ever appears. Either:

  • the service checks its own status in-process instead of shelling out to the CLI, or
  • the spawn passes windowsHide: true (i.e. CREATE_NO_WINDOW), or avoids the shell entirely by invoking the CLI with arguments rather than a command string.

Screenshot

No screenshot needed: the process-start trace above is the evidence. Terminal flashes are visible, but the parent chain is decisive.

Operating System

Windows 11 Pro, version 10.0.26300, build 26300 (x64).

Terminal

Windows Terminal (default terminal application); the spawned consoles are hosted by OpenConsole.exe from Windows Terminal 1.25.2733.0.

Workaround

Launching with opencode --standalone (private server, no background service) avoids it, at the cost of the background service itself.

Activity

  1. opencodejr commented on Nov 10, 2026

    @opencodejr
    Contributor

    Thanks for the very detailed trace!

    I think this is a duplicate of #52266. That issue tracks the same root cause: the background service (opencode serve --service) runs without a console on Windows, so any console child it starts without CREATE_NO_WINDOW gets its own visible window, which takes focus. The open PR #60471 addresses this by giving the service a windowless console, so unhidden children like your cmd.exe stay hidden.

    One thing worth knowing: I couldn't find any code in OpenCode 2.2.6 (or on dev) that runs opencode service status as a heartbeat. Core doesn't shell out to the CLI for its own status. The server entry points of oc-flight-deck and oc-plugin-view are empty stubs, so they aren't the source either. The cmd /d /s /c "opencode service status" command string most likely comes from something else loaded into the service: another server plugin, a local MCP server, a hook, or a project .opencode config. Look through your global and project opencode.json(c) and plugin directories for it; that would also stop the ~160 process starts per minute. If you find the source, please add it to #52266.

    Possible duplicates and related issues, ordered from most to least likely:

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

  2. nathwn12 commented on Nov 10, 2026

    @nathwn12
    Author

    Same root cause as #52266, and I accept the duplicate: the detached service has no console, so a console child it starts gets a new visible window, and that window takes the foreground. #60471's windowless-console change would fix the symptom no matter which code issues the spawn.

    One correction on attribution, because it changes where to look. In my trace the parent of the cmd.exe is the service itself, for every one of 24 starts in 45 seconds:

    cmd.exe /d /s /c "opencode service status"
      parent: opencode.exe serve --service
      parent: opencode.exe   (TUI)
      parent: opencode.exe   (launcher)
    

    It is a heartbeat, not a retry: opencode service status returns a healthy URL while it runs.

    If grep for the literal phrase finds nothing in core, the command is probably assembled from an argv pair, for example ["service", "status"] joined into a shell command line, which a search for the joined string would miss. Searching the service entry point for service near status would settle it faster than searching for the phrase.

    Plugins active during the capture: oc-plugin-view, oc-flight-deck, oc-stacked. oc-stacked registers no timers; its tools only run when called, and it produced no spawns in the trace. I could not find the phrase in any installed plugin source either.

    I am happy to re-run the WMI process-start trace against a build containing #60471 and report whether the windows stop; that would also confirm the fix independently of the attribution question.

  3. opencodejr commented on Nov 10, 2026

    @opencodejr
    Contributor

    Thanks for confirming, and for the careful follow-up on the attribution.

    I did search for "service" as a separate argument in the 2.2.6 source too, not only the full phrase. Apart from the CLI's own command definitions, the only code that builds a service … command is the desktop app's remote-access pairing (packages/gui-plugins/src/pairing/main.ts). It calls execFile(cli, [..., "service", ...args], { windowsHide: true }) with no shell, and only when pairing settings are read or changed. It can't produce a cmd.exe /d /s /c "opencode service status" child, and I found no shell-based spawn in core that runs this on a timer. So I still think the command string comes from something loaded into the service, not from core itself. As you say, though, #60471 fixes the visible window regardless.

    Your offer to re-run the WMI trace against a build with #60471 would be really useful. Please post the results on #60471 (or #52266) so they're in front of the people working on the fix. If the trace shows which module issues the spawn (for example, the service's loaded plugins and MCP servers at that moment), adding that there would settle the attribution question too.

    This issue will still be closed as a duplicate of #52266 as planned. Please follow that issue for updates.

  4. nathwn12 commented on Nov 10, 2026

    @nathwn12
    Author

    Correction to my last note, which I have deleted: strike the speculative part. What stands is measured:

    cmd.exe /d /s /c "opencode service status"
      parent: opencode.exe serve --service
    

    24 starts in 45 seconds (about one every 1.9 s), and the status call returns a healthy URL while it runs, so it is a heartbeat.

    I accept the duplicate of #52266. Same root cause, and #60471 fixes the symptom regardless of which code issues the spawn.

  5. opencodejr commented on Nov 10, 2026

    @opencodejr
    Contributor

    Thanks for the extra searching and for confirming. The measured part (a cmd.exe /d /s /c "opencode service status" child of serve --service about every 1.9 s, returning a healthy URL) is clear, and the root cause of the visible window is the one #52266 tracks.

    If you want to keep tracing the source: plugins can also load from the global ~/.config/opencode/plugins/ directory or from a project .opencode/ directory, not only from the plugins array in opencode.jsonc. Either could explain a plugin appearing that your config doesn't declare.

    This will be closed as a duplicate of #52266 as planned. Your WMI trace against a build with #60471, posted on that PR, would be a valuable check of the fix.

  6. neriousy commented on Nov 10, 2026

    @neriousy
    Member

    Thanks for the report. This is the same issue as #52266, so we're tracking it there. Closing as a duplicate. Please add any extra details on #52266.

  7. opencodejr commented on Nov 11, 2026

    @opencodejr
    Contributor

    Closing this as a duplicate of #52266, as discussed. Thanks for agreeing to it, and for the detailed process trace. The visible, focus-stealing console comes from the same root cause: the background service runs without a console, so its unhidden console children get their own window. The open PR #60471 addresses this whatever process issues the spawn.

    Please follow #52266 for updates. Your WMI trace against a build that includes #60471, posted on that PR, would be very welcome. If the windows keep appearing with that change in place, please say so there.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions