Skip to content

Fix broken failure telemetry: restore Clipper.logger assignment#657

Merged
wikirby merged 1 commit into
masterfrom
user/wikirby/fix-clipper-logger
May 12, 2026
Merged

Fix broken failure telemetry: restore Clipper.logger assignment#657
wikirby merged 1 commit into
masterfrom
user/wikirby/fix-clipper-logger

Conversation

@wikirby
Copy link
Copy Markdown
Contributor

@wikirby wikirby commented May 12, 2026

Summary

Wires up Clipper.logger in ExtensionBase — a static slot that two V2 module-level loggers depend on, but that V2 never set up. This is not a "normal telemetry is broken" PR: per-worker Aria logging for all user-initiated flows has been working fine. Only specific failure-scenario events were being silently dropped.

What WAS working (unaffected by this bug)

Per-worker Aria logging via LogManager.createExtLogger()AriaLoggerDecorator. All the normal observability:

  • InvokeClipper, ClipToOneNoteAction, HandleSignInEvent, GetNotebooks
  • All context properties (AppInfoId, BrowserLanguage, ClipperType, AuthType, UserInfoId, etc.)
  • Session start/end, click events, funnel events, trace events

These flow worker → Aria directly; they never touch Clipper.logger.

What was NOT working

Module-level functions that need a logger reference but have no this context (no instance to hold a logger on) reach for the static slot Clipper.logger. Affected call sites:

  • Log.ErrorUtils.sendFailureLogRequest — invoked from:
    • extensionBase.ts:316GetChangeLog (changelog fetch failure after version update)
    • extensionBase.ts:380UnhandledExceptionThrown (SW self.onerror — the safety net for any uncaught crash)
    • webExtensionWorker.ts:151UnclippablePage (scripting.executeScript probe failure)
  • userDataBoundaryHelper.ts:69JsonParse (sign-in EUDB endpoint returned malformed JSON)

Each of these called Clipper.logger.logFailure(...) on undefined, throwing TypeError: Cannot read properties of undefined (reading 'logFailure') inside the error handler itself. The browser silently swallows exceptions thrown by self.onerror (no recursive re-fire), so the failure was invisible — neither the original error nor the telemetry-mechanism error reached observability.

Most impactful loss: UnhandledExceptionThrown is the SW safety net for any unanticipated crash. Production telemetry has been blind to novel SW exceptions throughout V2.

Root cause

V1's src/scripts/clipperUI/clipper.tsx contained:

Clipper.logger = new CommunicatorLoggerPure(Clipper.getExtensionCommunicator());

That setter fired when the V1 Mithril sidebar bootstrapped via injection. V2 replaced the whole architecture — worker opens renderer window directly, no sidebar injection — and clipper.tsx stopped executing in the V2 flow. The V1 setter was never re-established in V2 and never fired again. The bug has been latent throughout V2 development. Commit 8f0dad5 later deleted the dead V1 source file, but the runtime behavior was already broken before that cleanup.

Why it stayed invisible until now:

  1. Affected paths fire only in error conditions — rare in normal usage and manual dev testing
  2. The TypeError thrown inside self.onerror is silently swallowed by the browser
  3. Telemetry-loss is silent by definition — nothing observable breaks user-facing flows

Fix

One line in ExtensionBase constructor, immediately after WorkerPassthroughLogger is created:

Clipper.logger = this.logger;

Routes module-level failure logs through the same per-worker AriaLoggerDecorator chain that normal telemetry uses.

Test plan

  • Build clean: npm run build:prod → zero errors, tslint passes
  • Verified bundle now has the assignment (grep "Clipper.logger\s*=" finds 1 match in the shipped JS)
  • SW DevTools repro: invoke clipper once to create a worker, then run setTimeout(() => { throw new Error("repro"); }, 0); in SW console → WebClipper.Failure.UnhandledExceptionThrown POST to browser.pipe.aria.microsoft.com/Collector/3.0/ confirmed with full Aria event payload (FailureType=Unexpected, Id=ExtensionBase, full stack)

Out of scope

The SW-boot window (errors fired before any per-tab worker exists → WorkerPassthroughLogger has zero fan-out targets) is a pre-existing architectural gap shared with V1 — V1's Clipper.logger was set inside the sidebar at injection time, so SW errors before sidebar load were also lost in V1. Filed as follow-up; full coverage would require an SW-level AriaLoggerDecorator independent of workers.

@wikirby wikirby force-pushed the user/wikirby/fix-clipper-logger branch from 4f02a93 to ab581b7 Compare May 12, 2026 08:41
Two V2 module-level loggers (Log.ErrorUtils.sendFailureLogRequest and
userDataBoundaryHelper) reach for the static `Clipper.logger` slot,
which V2 never set up. V1's clipper.tsx contained:

  Clipper.logger = new CommunicatorLoggerPure(...)

That setter fired when the V1 Mithril sidebar bootstrapped via
injection. V2 replaced the architecture (worker opens renderer window
directly, no sidebar injection) and clipper.tsx stopped executing in
V2's flow -- so the V1 setter never fired again, and no V2-equivalent
was established. The bug has been latent throughout V2 development.
Commit 8f0dad5 later deleted the dead V1 source file, but the runtime
behavior was already broken before that cleanup.

Normal Aria telemetry (InvokeClipper, ClipToOneNoteAction,
HandleSignInEvent, GetNotebooks, all context properties) is
unaffected -- those flow through per-worker AriaLoggerDecorator
instances that never touch Clipper.logger. The static-slot path is
only used by module-level functions that have no `this` context to
hold a logger on.

Affected failure-scenario events that were silently dropped:
  - UnhandledExceptionThrown (extensionBase self.onerror handler --
    SW safety net for any uncaught crash)
  - UnclippablePage (webExtensionWorker scripting.executeScript probe
    failure on chrome:// / about:// / file:// URLs)
  - GetChangeLog (changelog HTTP fetch failure after version update)
  - JsonParse (userDataBoundaryHelper EUDB endpoint malformed JSON
    during sign-in)

Each call into Clipper.logger.logFailure threw `TypeError: Cannot
read properties of undefined (reading 'logFailure')` inside the
error handler. The browser silently swallows exceptions from
self.onerror (no recursive re-fire), so neither the original error
nor the telemetry-mechanism error reached observability.

Fix: assign Clipper.logger = this.logger in the ExtensionBase
constructor immediately after WorkerPassthroughLogger is created.
Routes module-level failure logs through the same per-worker chain
that normal telemetry uses.

Verified via SW DevTools repro: after invoking the clipper once to
spawn a worker, throwing an unhandled exception in the SW console
now POSTs a WebClipper.Failure.UnhandledExceptionThrown event to
browser.pipe.aria.microsoft.com/Collector/3.0/.

The SW-boot window (failures before any worker exists, so
WorkerPassthroughLogger has zero fan-out targets) remains a
pre-existing architectural gap shared with V1 -- out of scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wikirby wikirby force-pushed the user/wikirby/fix-clipper-logger branch from ab581b7 to 6904bed Compare May 12, 2026 08:54
@aanchalbhansali aanchalbhansali self-requested a review May 12, 2026 15:29
@wikirby wikirby merged commit 9cb7fa7 into master May 12, 2026
3 checks passed
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