Skip to content

[WIP] Fix multiple firing of plugin events#3767

Closed
thdxr with Copilot wants to merge 5 commits into
devfrom
copilot/fix-multiple-plugin-events
Closed

[WIP] Fix multiple firing of plugin events#3767
thdxr with Copilot wants to merge 5 commits into
devfrom
copilot/fix-multiple-plugin-events

Conversation

Copilot AI commented Nov 2, 2025

Copy link
Copy Markdown
Contributor

Fix plugin events being fired multiple times

Root cause analysis:

  • Plugin.init() is called once per directory (cached in Instance)
  • However, Bus.subscribeAll() in Plugin.init() adds a NEW subscription each time it's called
  • The subscription callback is pushed to an array without checking if it already exists
  • This causes events to be fired multiple times (once per subscription)

Solution implemented:

  • Add a subscribed flag to track if subscription is already set up in plugin state
  • Only call Bus.subscribeAll() once per plugin instance by checking the flag
  • Add comment explaining the guard to prevent future issues
  • Run typecheck to ensure no type errors
  • Clean up temporary changes

The fix ensures that Plugin.init() is idempotent - calling it multiple times will only set up the event subscription once, preventing duplicate event firing.

Changes made:

  1. Modified packages/opencode/src/plugin/index.ts:
    • Added subscribed: false flag to plugin state
    • Guard Bus.subscribeAll() call with this flag to prevent multiple subscriptions
    • Added comment explaining the guard for future maintainers

This minimal change solves the issue reported in #[issue_number] where plugin events were being fired multiple times for each event.

Original prompt

This section details on the original issue you should resolve

<issue_title>plugin events are fired multiple times</issue_title>
<issue_description>### Description

Happening since the update - each plugin event is fired multiple times

OpenCode version

v1.0.11

Steps to reproduce

  1. Create a simple plugin that writes the event type into a file.
export const NotificationPlugin: Plugin = async ({ client, $ }) => {
  return {
    event: async ({ event }) => {
      await $`echo ${event.type} >> /tmp/events.txt`;
    },
  }
}
  1. Run opencode, and start a conversation. e.g. hi

  2. Look at the file in /tmp/events.txt

This is the output:

session.created
session.created
session.created
session.created
session.created
session.created
session.created
session.created
session.created
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
session.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
message.part.updated
...

</details>

- Fixes sst/opencode#3744

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 4 commits November 2, 2025 18:11
… state

Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
@github-actions

github-actions Bot commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Closing this pull request because it has had no updates for more than 60 days. If you plan to continue working on it, feel free to reopen or open a new PR.

@github-actions github-actions Bot closed this Feb 2, 2026
@thdxr
thdxr deleted the copilot/fix-multiple-plugin-events branch February 7, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants