rtb-telemetry 0.7.5

Opt-in anonymous usage telemetry with pluggable sinks. Part of the phpboyscout Rust toolkit.
Documentation
---
title: Errors
description: Every TelemetryError variant, its message, its diagnostic code and the calls that produce it.
---

# Errors

Everything fallible in the crate returns `Result<_, TelemetryError>`.

## The variants

| Variant | Message | Diagnostic code | Raised by |
|---|---|---|---|
| `Io(std::io::Error)` | `sink I/O error: {0}` | `rtb::telemetry::io` | `FileSink` writes; `consent::read`, `write`, `reset` |
| `Serde(String)` | `serialisation error: {0}` | `rtb::telemetry::serde` | Event JSON serialisation in `FileSink` / `OtlpSink`; malformed or wrong-version consent TOML |
| `Http(String)` | `HTTP telemetry sink error: {0}` | `rtb::telemetry::http` | `HttpSink`: endpoint scheme, client build, transport, non-2xx |
| `Otlp(String)` | `OTLP telemetry sink error: {0}` | `rtb::telemetry::otlp` | `OtlpSink`: endpoint scheme, exporter build, flush |

`TelemetryError` is `#[non_exhaustive]`, so a `match` over it needs a `_`
arm and new variants can appear in a minor release.

`Io` is the only variant carrying a typed cause; the rest carry a string,
already formatted by the sink that produced it. `Io` has
`#[from] std::io::Error`, so `?` on a filesystem call inside a custom sink
converts for free.

## Diagnostics

`TelemetryError` derives `miette::Diagnostic` as well as
`thiserror::Error`, so the codes above show up in a miette-rendered report.
Any tool already returning `miette::Result` from `main` can propagate a
telemetry error with `?` and get the code in the rendered output.

## Which failures reach the caller at all

- **`TelemetryContext::record` under `Disabled`**: always `Ok(())`. The
  sink is never called, so it cannot fail.
- **`FileSink` and `HttpSink`**: report their failure from `emit`, and
  `record` passes it straight back to you.
- **`OtlpSink`**: `emit` returns `Ok(())` even when the export fails.
  Export errors only appear from `flush`. See
  [Sinks]sinks.md#emit-never-reports-an-export-failure.
- **`MemorySink`**: never fails. If its internal lock is poisoned it drops
  the event and still returns `Ok(())`.

## What to do with a telemetry error

Recording usage is not the job the user asked for, so failing their command
because a telemetry write failed is almost always the wrong call. Log it,
or ignore it deliberately:

```rust
if let Err(err) = telemetry.record("command.invoke").await {
    tracing::debug!(%err, "telemetry record failed");
}
```

The exception is a tool whose *purpose* is shipping telemetry, where a
failed export is the failure of the run.

Two things make these errors safer to log than they might look. The
out-of-process sinks redact `args` and `err_msg` *before* serialising, so
an error raised during the write is describing an already-redacted event.
And `SecretString` keeps bearer tokens and OTLP header values out of
`Debug` output. Attributes are not redacted anywhere, so an error string
that ends up quoting your own attribute values is only as safe as what you
put in them.