---
title: Request-scoped diagnostics
description: "The per-request diagnostic accumulator (DiagnosticContext), the Diagnostic record, the DiagnosticSeverity enum, and the dev-mode overlay plus X-Pennington-Diagnostic header that surface them."
canonical_url: https://usepennington.net/reference/diagnostics/request-context/
sidecar_url: https://usepennington.net/reference/diagnostics/request-context.md
content_hash: sha256:32a0fca27dc9821ee8aa6fcf708f0d1a1f58a63520d122866c23eb9d1db3611e
tokens: 1344
uid: reference.diagnostics.request-context
reading_time_minutes: 3
---

Reference
# Request-scoped diagnostics

The per-request diagnostic accumulator (DiagnosticContext), the Diagnostic record, the DiagnosticSeverity enum, and the dev-mode overlay plus X-Pennington-Diagnostic header that surface them.

 
The scoped accumulator and record types that collect per-request warnings, errors, and info messages, plus the two transports that surface them: the `X-Pennington-Diagnostic` response header (emitted on every request, including during a static build, where it feeds the [build report](https://usepennington.net/reference/api/build-report.md)) and the dev-only on-page overlay. The accumulator and record types live in `Pennington.Diagnostics`; the header emission and overlay processor live in `Pennington.Infrastructure`.

 
## `DiagnosticContext`

 
Per-request accumulator registered as `Scoped`. Not thread-safe; resolved via DI for the lifetime of one request.

 
### Members

 
    Member Description     `Add(Diagnostic diagnostic)` Appends a pre-constructed `Diagnostic`.   `AddError(string message, string? source = null)` Appends a `Diagnostic` with `Severity = Error`.   `AddWarning(string message, string? source = null)` Appends a `Diagnostic` with `Severity = Warning`.   `AddInfo(string message, string? source = null)` Appends a `Diagnostic` with `Severity = Info`.   `Diagnostics` Read-only view of the diagnostics accumulated so far, in insertion order.   `HasAny` `true` when at least one diagnostic has been appended.   `HasErrors` `true` when at least one appended diagnostic has `Severity = Error`.    
## `Diagnostic`

 
Immutable record carrying one diagnostic event. Route-agnostic — `HttpContext` supplies the route context.

 
### Parameters

 
    Name Type Default Description     `Severity` `DiagnosticSeverity` — Severity band controlling overlay color and the `HasErrors` flag.   `Message` `string` — Human-readable body rendered into the overlay panel and, percent-encoded, into the second segment of the `X-Pennington-Diagnostic` header value.   `Source` `string?` `null` Optional producer label (for example, `"XrefResolver"`); rendered as the overlay pill subtitle and, percent-encoded, appended as a third header segment when non-null.    
## `DiagnosticSeverity`

 
Three-value enum.

 
### Values

 
    Name Value Description     `Warning` `0` Recoverable issue (for example, an unresolved xref); contributes to the overlay warning count and the amber badge color.   `Error` `1` Failure that indicates broken content or misconfiguration; flips `HasErrors` and renders with the red badge color.   `Info` `2` Informational notice about degraded but non-broken behavior; does not contribute to the error or warning counts.    
## `X-Pennington-Diagnostic` header and dev-mode overlay

 
Two transports surface the accumulated diagnostics, both wired inside `UsePennington` via the response-processor pipeline. The header is emitted on every request; the overlay is dev-only.

 
    Transport Type Availability Shape     Response header `X-Pennington-Diagnostic` Every request where `HasAny` is `true`, including during a static build (the build pipeline parses these headers and folds them into the [build report](https://usepennington.net/reference/api/build-report.md)) One header value per diagnostic, pipe-delimited: `Severity|Message` (or `Severity|Message|Source` when `Source` is non-null). Each segment is independently percent-encoded with `Uri.EscapeDataString` so non-ASCII values (accented locale names, content titles) survive the ASCII-only header writer — a machine parser must split on `|` then `Uri.UnescapeDataString` each segment.   On-page overlay `DiagnosticOverlayProcessor` (`Order = 30`, `IResponseProcessor`) Dev-serve requests (the host was not launched with the build verb) where status is `2xx` and content type contains `text/html` Floating badge injected before `</body>` summarizing error/warning counts; clicking expands a panel listing every diagnostic. Re-renders on the `spa:diagnostics` DOM event for SPA navigations.    
## Example

 
```csharp:symbol,bodyonly
public int Order => 50;
  
public bool ShouldProcess(HttpContext context)
{
    if (context.Response.StatusCode is < 200 or >= 300)
    {
        return false;
    }
  
    var contentType = context.Response.ContentType;
    return contentType is not null
           && contentType.StartsWith("text/html", StringComparison.OrdinalIgnoreCase);
}
  
public Task<string> ProcessAsync(string responseBody, HttpContext context)
{
    if (!responseBody.Contains("rel=\"canonical\"", StringComparison.OrdinalIgnoreCase))
    {
        diagnostics.AddWarning(
            "Page is missing a <link rel=\"canonical\"> tag.",
            source: context.Request.Path);
    }
    return Task.FromResult(responseBody);
}
```

 
## See also

 
 - Related reference: [Build report fields](https://usepennington.net/reference/api/build-report.md)
 - Related reference: [Response processing interfaces](https://usepennington.net/reference/api/i-response-processor.md)
 - How-to: [Write a response processor](https://usepennington.net/how-to/response-pipeline/response-processor.md)
 
 
[Previous
                
                CLI and build arguments](https://usepennington.net/reference/host/cli.md)[Next
                    
                Built-in BlogSite routes](https://usepennington.net/reference/blogsite/routes.md)