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

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions