Skip to content

WinMLCLIError telemetry loses root cause — ClickException wrappers make errors unanalyzable #1110

Description

@timenick

Problem

WinMLCLIError records exception_type, a scrubbed+capped exception_message, and a structured exception_stack. In practice, commands wrap the real failure in a click.ClickException before it reaches telemetry, so the recorded event loses the information needed to analyze the failure.

Two failure shapes observed in production telemetry:

1. Root cause buried in a truncated message

Command:      inspect
ErrorType:    ClickException
ErrorMessage: Inspection error: The checkpoint you are trying to load has model
              type `qwen3_5` but Transformers does not recognize this
              architecture. This could be because of an issue with the
              checkpoint, or becau…      <- truncated at the 200-char cap
Count:        7

The real exception (a Transformers ValueError) is stringified into the message at commands/inspect.py:328 (raise click.ClickException(f"Inspection error: {e}") from e). ErrorType collapses to ClickException, so distinct root causes can't be grouped, and the useful text is cut off by the 200-char cap.

2. No information at all

Command:      quantize
ErrorType:    ClickException
ErrorMessage: Quantization failed
Count:        3

commands/quantize.py:327 raises click.ClickException(f"{label} failed") with no e and no from e. The root cause is dropped at the source — telemetry has nothing to record.

Impact

  • ErrorType is almost always ClickException, so errors can't be grouped or triaged by real cause.
  • The most diagnostic signal is forced into the free-text exception_message, which is the least reliable field (PII-scrubbed and capped at 200 chars).

Proposed direction (two parts)

1. Telemetry layer (broad, command-agnostic)

In Telemetry.log_error (telemetry/telemetry.py:212), walk the __cause__ / __context__ chain and record the root cause alongside the outer exception, e.g. new root_cause_type (and optionally a scrubbed root_cause_message) attributes. This immediately recovers shape #1 for every command without touching command code, and stays architecture-agnostic (no hardcoded command/exception names).

2. Command layer (root fix, per-command)

raise click.ClickException("X failed") sites that drop the cause must preserve it — carry e + from e, or (where there is no exception object, e.g. a failed sub-step return code) attach the actual failure reason (exit code / stderr / status). Known offenders: commands/quantize.py:327, :457, :469. Prioritize by frequency using the telemetry data itself.

Recommended order: land (1) first (single change, wide coverage), then use the resulting telemetry to find and fix the highest-frequency information-less sites in (2).

Related: the 200-char exception_message cap (telemetry/utils.py:116) may warrant a modest increase, subject to the backend field-length limit and the "conservative over leaky" PII stance.

Metadata

Metadata

Assignees

Labels

P1High — major feature broken or significant UX impactbugSomething isn't workingqualityUse for quality control related issuestriagedIssue has been triaged

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions