You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Config hot-reload in a long-lived multi-location server permanently fails all later plugin loads ("instances must use identical telemetry configuration") #140
acquireSharedOtel pins the shared SDK singleton on globalThis.__opencode_plugin_otel_shared__ with the configKey of the first successful load in the process, and that key is never re-pinned. When opencode hot-reloads configuration (any opencode.json edit), it re-runs plugin setup once per loaded location. In a long-lived server hosting many locations, one mid-process edit to any otel-relevant option makes every subsequent load throw:
failed to load plugin devtheops.otel
Error: OpenCode OTel plugin instances must use identical telemetry configuration within one process
Timeline observed (local time, single opencode serve process, ~40 loaded locations):
19:23 — opencode.json edited (config-sync tooling rewrites it several times through the evening)
19:29 — first failed to load plugin devtheops.otel after the watcher hot-reloads config; ~40 WARNs per edit, once per location
19:29–21:56 — 1,867 WARNs total, tracking each config edit
21:56 — options return to the originally-pinned values → loads succeed again, no code change
Repro (minimal)
Start opencode serve (any long-lived host; desktop --service shows the same) with @devtheops/opencode-plugin-otel enabled via global opencode.json and ≥2 project locations loaded.
Edit opencode.json, changing any otel-relevant option (e.g. append to disabledMetrics).
Watcher hot-reload → failed to load plugin devtheops.otel … must use identical telemetry configuration within one process for every location.
Revert the edit → loads succeed again (the pinned key matches once more). A restart is otherwise required.
Why it happens (dist/index.js, v2.0.1)
configKey() (~L70534) hashes the resolved config; acquireSharedOtel() (~L70555) pins globalThis.__opencode_plugin_otel_shared__ on first successful load and throws on any later mismatched key — including the pending-promise path (~L70583).
releaseSharedOtel() (~L70588) only decrements refs with a Math.max(0, …) clamp; the plugin's cleanup fn (~L72253) calls it, but the singleton and its pinned configKey are never cleared.
So the first-ever config in the process wins forever; any intentional otel config change mid-process fails all subsequent loads until restart — even though a config reload is exactly the moment an operator expects the new config to apply.
The intentional part is fine — failing on genuinely conflicting multi-location config prevents exporting one location's data using another location's settings (README, "Configuration"). The gap is that a uniform, deliberate config change can never take effect without a process restart, and the error carries no hint of which field diverged.
Suggestions
When refs reaches 0 in releaseSharedOtel, flush + shut down providers and clear globalThis[OTEL_KEY] so the next load re-pins fresh config (makes hot-reload converge).
Or on key mismatch, replace the singleton (drain old exporters, re-init with the new config) rather than throwing — a config change should win over the first-pinned snapshot.
At minimum, include the differing field(s) or both key hashes in the error message — today the message gives nothing actionable.
Environment: macOS 15, opencode 2.1.4 (both opencode serve and desktop serve --service), plugin 2.0.1, protocol http/protobuf, single global config source.
Activity