Median

Crash reports

File the crash your error screen shows as a bug in Signal, with one press.

Updated Oct 1, 20264 minute read
app/error.tsx
"use client";

import { reportError, useCanReportError } from "@mediansh/widget";
import { useState } from "react";

export default function ErrorScreen({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  const canReport = useCanReportError();
  const [note, setNote] = useState<string | null>(null);

  async function report() {
    const outcome = await reportError(error);
    setNote(outcome.reported ? "Reported." : outcome.reason);
  }

  return (
    <div>
      <p>Something went wrong.</p>
      <button onClick={reset}>Try again</button>
      {canReport && note === null && (
        <button onClick={report}>Report error</button>
      )}
      {note && <p>{note}</p>}
    </div>
  );
}

reportError files the error as a bug in Signal, with the page and a screenshot. With diagnostics on, the browser's recent errors come too. It never throws. medianSupport.reportError and useMedianSupport().reportError are the same function.

Requirements

  • A key. MedianSupport, MedianSupportModal, MedianFeedback, MedianContactForm and useMedianContact each record their key as they mount. reportError uses the last one recorded on this page load. Pass apiKey when none has mounted.
  • Track bugs and suggestions turned on under Agent → Behavior. With it off, nothing is filed.
  • A browser. On the server it returns a reason and sends nothing.

The key is held in memory, so it outlives an error boundary that replaced the tree the widget was in. A full page load forgets it.

In global-error.tsx, pass apiKey. A crash on the first page load can happen before any widget mounts.

app/global-error.tsx
const outcome = await reportError(error, {
  apiKey: process.env.NEXT_PUBLIC_MEDIAN_PUBLIC_KEY,
});

NEXT_PUBLIC_MEDIAN_PUBLIC_KEY holds the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY). The id is safe in client code.

Options

NameTypeDescription
apiKeystringThe median_pk_ id. Defaults to the key of the last Median component that mounted.
userMedianUserWho reported it. Defaults to the user of the last Median component that mounted. A signed id and hash tie the report to the person.
routestringYour router's name for the page, such as /orders/[id]. Leads the title. Cut at 200 characters. Default: window.location.pathname.
componentStackstringReact's component stack, when your boundary has one. Default: error.componentStack.
stateRecord<string, string | number | boolean>Facts for the report, placed before appState and winning any clash of keys. The same limits as appState.
attachRecord<string, unknown>Blocks to attach, as { label: value }. Added after anything queued with attach(). The same limits as attach().
telemetrybooleanfalse leaves out time zone, language, browser, OS, device, screen, and the page title and referrer. The page URL still comes with the diagnostics snapshot. Default: true.
screenshotbooleanfalse files the report without a picture of the screen. Default: true.

Limits for state and attach are on Context and diagnostics.

Outcome

type MedianErrorReportOutcome =
  | { reported: true; joined: boolean }
  | { reported: false; reason: string };

joined is true when the report joined a bug that was already filed.

reasonWhen
Median has no API key on this page, so the crash could not be reported. Mount <MedianSupport> or pass apiKey to reportError.No Median component has mounted on this page load, and no apiKey was passed. Also printed to the console
reportError only runs in a browser.Called on the server
Error reporting is currently unavailable. Try again later.Track bugs and suggestions is off
The report could not be sent. Check the connection and try again.The request failed, for example offline, rate limited, or a revoked key. The console has the cause

A report that does not land puts queued attach() entries back, together with the entries from the attach option. A second press carries them, and so does the next message, note or contact form send.

What lands on the report

On the reportValue
Kind and priorityBug, High
TitleCrash on /orders/[id]: TypeError: Cannot read properties of undefined. Without a route, Crash: TypeError: ...
Write-upThe page stopped working on the route and somebody reported it from the error screen, with Error and Digest lines
QuoteReported from the error screen: followed by the error
Crash traceerror.stack
Component stackcomponentStack
RouteThe route option. Left out when the page URL already ends with it, which is the case for the default
Digesterror.digest from a Next.js server error
Page and ViewportThe page URL without its query string, and the window size
App state rowsRoute, Digest and state, then appState from the last Median component given one
Browser logsRecent errors, when a Median component on the page had diagnostics on
ScreenshotThe error screen. Median panels are left out
Your blocksEntries queued with attach(), then the attach option
ReporterThe visitor. Their browser and device go on their customer profile
  • The title leaves out the error's name when it is Error or Exception, or when the message already starts with it.
  • The message is cut at 300 characters. An error with no message is Unnamed error.
  • Stacks are cut at 4,000 characters. The name and the digest are cut at 60.
  • Route, Digest and state come first among the 20 app state entries, so a full appState loses its last entries rather than the crash's.
  • The report is filed at once. No model reads it first.
  • A new bug is then checked against the list like any other, may be merged into a matching one, and goes to your connected issue tracker. See Signal.

Joining

  • A report joins a bug with the same title, unless that bug was declined. Case and punctuation are ignored. Only the 60 most recent bugs that were not declined are compared.
  • The same error on the same route gives the same title, so many people hitting one crash make one bug with one reporter row each.
  • A visitor who reports the same crash again updates their row.

useCanReportError

const canReport = useCanReportError();
ReturnsWhen
falseOn the server and during hydration
falseNo Median component with a key has mounted on this page load. With identity only, until the route answers
trueA Median component has mounted with a key
  • It turns true by itself when a component mounts. You do not need to re-render.
  • It does not check Track bugs and suggestions. A press can still return the unavailable reason.
  • It ignores the apiKey option. If you always pass apiKey, show the button without it.

Crash reports share the widget's rate limit for sends. See Conversations.

Still need help?

    Esc