agentskills.codes
DI

diagnostics

Error handling and build-time diagnostics conventions - Result-not-panic, structured Diagnostics with stable codes, actionable help, color/JSON presentation layering, exit codes, and cold-path performance.

Install

mkdir -p .claude/skills/diagnostics && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16628" && unzip -o skill.zip -d .claude/skills/diagnostics && rm skill.zip

Installs to .claude/skills/diagnostics

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Error handling and build-time diagnostics conventions - Result-not-panic, structured Diagnostics with stable codes, actionable help, color/JSON presentation layering, exit codes, and cold-path performance.
205 charsno explicit “when” trigger

About this skill

Error Handling & Diagnostics

Use this skill whenever you add, change, or review an error path: a build-time authoring error, a parser/handler failure, a CLI validation error, or anything surfaced to a host (FFI/WASM/Node) or a tool/agent. WebUI errors must be recoverable, actionable, and machine-consumable — for humans and AI agents alike.

1 - Never panic on recoverable input

panic = "abort" in the release profile means a panic kills the process instantly — including any FFI/WASM/Node host embedding the framework. Bad template input, bad CLI input, and bad state are recoverable and must return Result, never panic!/unwrap()/expect().

SituationDo
Malformed template / CSS / route authored by a developerReturn Result with a structured Diagnostic (see §3).
Missing/invalid CLI input (file, port, flag)Return a typed CliError (see §6).
A genuinely impossible internal statePrefer ? with a typed error; only use unreachable!/expect with a justification comment, and never in a hot or host-reachable path.

unwrap()/expect() are banned in library code (clippy.toml disallowed-methods). todo!, unimplemented!, and dbg! are banned workspace-wide (clippy.toml disallowed-macros). Tests opt out with #[allow(clippy::disallowed_methods)].

Enforcement note. unwrap/expect/todo!/unimplemented!/dbg! are caught by clippy. panic! is not lint-banned (too entrenched) — keep it out of recoverable paths by review. The "no regex in core logic" rule is also review-enforced, not deny-banned: actix-web pulls regex transitively, so a crate-level ban would break the build and can't scope to first-party code.

2 - Error type conventions

Crate kindError type
Library (webui-parser, webui-handler, webui-expressions, webui-state, webui-protocol, webui-ffi)Custom enum via thiserror.
Binary (webui-cli, xtask)anyhow for orchestration; a typed enum when callers must branch on the cause (e.g. webui-cli's CliError for hints + exit codes).
  • Each error layer's Display describes only its own level; the #[source] chain carries the rest, so anyhow's {:#} never double-prints. Provide a flat chain_message() helper for hosts that don't walk the chain (Node, FFI).
  • Add dedicated variants instead of overloading a generic Generic(String) / Validation(String) so callers can match programmatically.

3 - Authoring errors are structured Diagnostics

Every "the developer wrote invalid template syntax" mistake is returned as ParserError::Template(Box<Diagnostic>) (crates/webui-parser/src/diagnostic.rs), so all build errors render identically. A Diagnostic carries:

  • code — a stable, machine-readable identifier (e.g. invalid-for-each). Defined in diagnostic::codes. Treat codes as a stable API: tools and AI agents branch on them, so rename only with a deliberate migration.
  • title — short, lowercase (invalid <for> each expression).
  • location — set from a byte offset via .at_offset(source, offset); rendered rustc-style --> owner:line:column (single forward scan, no regex, no recursion), falling back to in component <c> · element <e>.
  • snippet — the offending source text.
  • help: — an actionable fix (see §4).

Add a new authoring error with the parser helpers (authoring_error, authoring_error_at, html_error) and a new constant in diagnostic::codes. Validate at parse/build time and fail fast — never defer to render time.

4 - Make errors actionable (and typo-aware)

Tell the developer what is wrong and how to fix it. Every Diagnostic should carry a help: line. Where a mistake is likely a typo, suggest the intended name via suggest::closest_match (iterative Levenshtein — no recursion, no regex, cold path only):

  • Misspelled directive attribute: <for eahc=…> -> "did you mean each?"
  • Unknown component tag: suggest the closest same-namespace registered component (<mp-buton> -> <mp-button>). Prefix-guard the match (text before the first - must match) so a genuine third-party custom element (<md-button>) is never falsely flagged.

5 - Presentation layering: color belongs ONLY in the entry point

Libraries produce plain, color-free data. The entry point decides how to present it. Never embed ANSI in library output or in any machine/host channel.

ConsumerGets
webui-cli (terminal)Reads Diagnostic fields and colorizes with console::style() — the only approved styling method (see copilot-instructions "Terminal output styling").
FFI / WASM / NodeThe plain Display text through their native error channel (webui_last_error, JsValue, napi::Error).
Browser / tools (dev-server live-reload, SSE, console.error)Plain text. ANSI renders as garbage and breaks single-line SSE frames.

When one value feeds both a terminal and a non-terminal channel, split it: webui-dev-server's RebuildError { display, message } carries a colorized display for the reporter and a plain message for the browser.

Per-line color: when colorizing multi-line output, style each line independently (open + close the SGR span within the line). A single span that straddles newlines bleeds when the line is later re-prefixed (e.g. [server] under xtask dev).

6 - Machine-readable output and exit codes (webui-cli)

For editors, CI, and AI/agent tooling:

  • --format json (global flag) emits each error as one JSON object on stdout (no ANSI; decorative output suppressed): {severity, code, message, file, line, column, snippet, help, chain}. Branch on the stable code, not the human message. Build the object with the serde_json::Map API, not the json! macro (it unwraps internally and trips disallowed_methods).
  • Exit codes follow BSD sysexits.h (webui-cli's error::exit_code): 65 data/authoring error, 66 missing input, 69 port in use, 74 I/O, 2 usage (clap), 1 otherwise.
  • Replace fragile err_msg.contains("...") dispatch with typed errors that own their hint() and exit_code().

7 - Error construction is COLD - keep it off the hot path

Building a Diagnostic (format strings, suggestions, location scans) is rare, but if it inlines into a hot function it bloats that function and perturbs its code layout — a real, measurable regression (observed ~4-5% on parse benches with no added hot-path work).

  • Mark error builders #[cold] + #[inline(never)] (e.g. authoring_error*, html_error, css_diagnostic, *_error constructors, suggest::closest_match).
  • Keep hot fast-paths inlinable: a per-element check (e.g. split_once('-')) must stay inlined; only its cold fallback (the registry scan) goes out-of-line.
  • Validate layout-sensitive changes with cargo bench -p <crate> against the base branch (see skills/perf/SKILL.md). A "regression" with no added compute is usually layout — fix it with #[cold], don't shrug it off.

8 - Checklist for a new error

  • Returned as Result, never panicked (host-safe under panic = "abort").
  • Authoring mistake -> ParserError::Template(Box<Diagnostic>) with a new diagnostic::codes constant, location, snippet, and help:.
  • help: is actionable; add a "did you mean …?" suggestion if it's a typo.
  • No color/ANSI in library, host, browser, or JSON output.
  • Surfaced in --format json with the stable code; exit code classified.
  • Error construction is #[cold]/#[inline(never)]; hot path unchanged (confirm with a benchmark if it sits near a hot loop).
  • Tests: a regression test that fails without the error, asserting on the code (not the prose); JSON stays plain (no \x1b).
  • DESIGN.md and docs/ (incl. docs/ai.md) updated if the contract or a user-visible code/flag changed.

More by microsoft

View all by microsoft

Search skills

Search the agent skills registry