Skip to content

Fix Enterprise Managed OTel Race in Copilot Extension - #336701

Merged
Ross Wollman (rwoll) merged 6 commits into
mainfrom
rwoll/otel-late-policy-recovery
Sep 18, 2026
Merged

Ross Wollman (rwoll) merged 6 commits into
mainfrom
rwoll/otel-late-policy-recovery

Conversation

@rwoll

@rwoll Ross Wollman (rwoll) commented Sep 18, 2026

Copy link
Copy Markdown
Member

Enterprises use Managed Settings to configure OTel for Copilot chat sessions. Previously, an initialization race could leave the Copilot extension running with its original OTel configuration after the enterprise configuration arrived. In particular, export could remain off for the rest of the window even though Settings showed it enabled.

Fix

Compare current OTel settings against the configuration used to construct the running service, both after activation and on OTel configuration changes. If a late enterprise configuration enables OTLP export that was initially off, show a progress notice and request an extension-host restart to rebuild the telemetry service with the managed configuration.

Why restart the host? Replacing just the OTel object would leave existing consumers, event subscriptions, and the embedded runtime using old state. In-process recovery is feasible, but needs a coordinated reinitialization path. For this Local-harness hotfix, we reuse the existing host-restart lifecycle and limit it to one automatic attempt per session.

Recognized enterprise OTel settings replace the whole personal OTel settings block, rather than merging with it. Fields omitted by the enterprise use defaults. Other settings are unaffected.

Restart behavior

  • No managed OTel configuration: no automatic restart. Personal settings edits and unrelated enterprise policies do not change the policy-backed OTel defaults used by this guard. An enterprise subscription alone is not sufficient.
  • Policy available at initialization: no restart; the service already uses that configuration.
  • Policy arrives late: at most one automatic recovery attempt per workspace/editor session. This will commonly be around startup, but can also follow a later sign-in.
  • Later policy changes or repeated refreshes: no further automatic restart. Changed settings offer an opt-in reload; unchanged values do nothing. There is no polling or periodic restart.
  • Future launches: a restart is not automatic on every launch. A new session can recover again only if the late-arrival case recurs.

An eligible restart affects other extensions in the window and can interrupt work. It is preceded by an auto-clearing progress notice and respects existing active-session shutdown vetoes. Successful recovery is logged without a confirmation toast. A failed or declined attempt falls back to a manual reload, not repeated automatic attempts.

Accepted gaps and rationale

Telemetry produced before policy arrives and recovery completes is not guaranteed to be exported. This is not limited to a first-ever run, and we do not assume policy always arrives before the first chat request. Policies indistinguishable from schema defaults remain a limitation of using the existing configuration API.

Environment-variable precedence is unchanged: overrides remain an accepted risk. The package.json descriptions correct the documentation, not the behavior. Full enterprise-over-environment enforcement across the resolver, exporters, and embedded SDK is separate work.

This is a tactical fix for the Local harness while moving toward Agent Host. A proposed policy-inspection API would improve provenance but would not make policy arrive earlier or reconfigure an initialized service. Holding activation would require a readiness signal before OTel initialization and defined failure/timeout behavior without delaying unmanaged users. Those broader startup/API changes are deferred rather than added to this hotfix.

The comparison/fingerprint covers the resolved configuration and github.copilot.chat.otel.{enabled,exporterType,protocol,otlpEndpoint,captureContent,serviceName,resourceAttributes,headers,outfile,maxAttributeSizeChars,dbSpanExporter.enabled} defaults. The last two are tracked but are not policy signals.

Manual Testing

Manual Test Steps

Use the single-script macOS repro, which combines a console collector and temporary managed-settings file. Requires macOS, Node.js 18+, and a free port 4318.

  1. Start without managed OTel. Use a disposable test setup and an approved account with no existing OTel policy from the account or MDM. /Library/Application Support/GitHubCopilot/managed-settings.json must not exist; the script refuses to overwrite it. Close other VS Code windows because this policy is machine-wide. Leave personal OTel and DB export disabled and clear OTel environment overrides from the test launcher. A fresh profile alone does not isolate machine policy.

  2. Activate Copilot before applying policy. Open the patched extension on Insiders (--extensionDevelopmentPath /path/to/vscode/extensions/copilot) or the local build (./scripts/code.sh). Sign in and select the Local harness, not Agent Host. Send a harmless request such as "Reply with OK" while OTel is still off, ensuring Copilot has activated.

  3. Run the gist in one terminal. Download otel-restart-test.cjs and run:

    sudo node otel-restart-test.cjs

    The collector starts immediately. Press Enter only after step 2, then switch back to VS Code. The script deliberately waits 8 seconds to make late policy arrival easy to observe; this is a test delay, not an estimate of production latency. It then prints Test policy applied for 120 seconds. and enables managed OTel pointing at the collector. The automatic extension-host restart is usually near-instant: you may only glimpse a brief progress notification, which dismisses automatically as the host restarts. No Developer: Sync Account Policy command is needed.

  4. Verify export. Once Copilot chat is ready, send another harmless Local request and allow about 10 seconds for batching. The script's terminal should print POST /v1/traces and JSON data. There is no success toast; recovery is recorded in the Copilot output log. Use harmless prompts and review printed telemetry before sharing.

Cleanup: After 120 seconds, or on Ctrl+C, the script removes only its unchanged test file and stops the collector. Do not edit that file during the run. Policy withdrawal should offer a manual reload, not another automatic restart. Reload or close the test window afterward; use a fresh editor session to repeat the test.

Before-fix comparison: Repeat the same flow on an unpatched build. Policy becomes visible, but there is no automatic restart or new trace export until a manual reload.

Additional controls: Without enterprise OTel policy, personal settings edits should only offer a reload. To check settings replacement, repeat with personal service-name/resource-attribute values configured before the test; these must not appear in the printed managed telemetry.

This deliberately delivers policy after activation. The unit regression separately covers policy arriving between service construction and watcher registration.

Resolves #336102.

Preserve the event-driven, single-attempt recovery implementation and OTel settings-block replacement before evaluating an in-process alternative. Refs #336102.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 18, 2026 05:34
@rwoll

Copy link
Copy Markdown
Member Author

I'm investigating whether we can restart/reinitialize the OTel service in-process instead of restarting the extension host, without introducing significant destabilization risk for this release. Keeping this PR in draft while I assess that alternative.

This comment was marked as outdated.

@github-actions

This comment was marked as low quality.

@rwoll

Copy link
Copy Markdown
Member Author

restart/reinitialize the OTel service in-process instead of restarting the extension host

Prototyped this. The extension exporter can initialize in-process, but the embedded SDK captures configuration during startup and ignores updates after native telemetry initializes. Coordinating both safely needs broader lifecycle work, so we are keeping the bounded host restart for this release.

Track recognized managed settings independently of export enablement so later updates to a disabled startup policy only offer reload. Add regression coverage and align OTel Settings descriptions with the retained environment-variable precedence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"name": "CopilotOtelEnabled"
},
"markdownDescription": "Enable OpenTelemetry trace/metric/log emission for Copilot Chat operations. Precedence: enterprise policy > env var `COPILOT_OTEL_ENABLED` > user setting. Requires window reload.",
"markdownDescription": "Enable OpenTelemetry trace/metric/log emission for Copilot Chat operations. Recognized enterprise OTel settings replace personal OTel settings, but env var `COPILOT_OTEL_ENABLED` can still override enablement. Requires window reload.",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are documentation-only edits. Environment variables can already override or supplement managed OTel settings; this PR does not introduce that behavior. Full enterprise-over-environment enforcement across the resolver, exporters, and embedded SDK is separate work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This comment was marked as resolved.

Use a lifecycle-bound progress notification instead of a persistent warning so successful restart cannot leave contradictory notices. Preserve the existing restart guard and fallback grace, and cover command failure, fallback, and unmanaged settings in contribution tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the redundant success notification while retaining the persisted restart acknowledgement, output log, monitoring indicator, and manual reload warnings. Add regression coverage for silent success and update the monitoring guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rwoll

Ross Wollman (rwoll) commented Sep 18, 2026

Copy link
Copy Markdown
Member Author

Draft implementation: #336814, stacked on this PR. It adds governed identity capture for Local and now includes the shared telemetry-block precedence fix. The follow-up still needs core validation and a live editor smoke test; the draft is not rollout-ready. Review and signed approval remain outstanding.


### Activation

When late enterprise OTel settings turn on external export after Copilot's telemetry service

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is all this text necessary or could it be more concise?

@rwoll
Ross Wollman (rwoll) merged commit 229224f into main Sep 18, 2026
34 of 35 checks passed
@rwoll
Ross Wollman (rwoll) deleted the rwoll/otel-late-policy-recovery branch September 18, 2026 20:03
@vs-code-engineering vs-code-engineering Bot added this to the 1.139.0 milestone Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Issue identified by VS Code Team member as probable bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copilot managed OTel policy can be missed during extension activation

3 participants