Everything you need to capture production errors, open GitHub issues, and hand your agent source-aware repair context.
Using Claude Code, Codex, Antigravity, or another agentic editor?
Point the agent at the Patchly skills guide so it can install the SDK, wire browser and server capture, and upload source maps without guessing. Project-specific skills URLs are available from each project's settings page.
npm install patchly@latestMinimum version: patchly@0.2.2. Earlier releases are missing serverless flush support, breadcrumbs, network/console auto-capture, the React error boundary, or the no-op-on-missing-DSN guard. If you have an older version pinned, run npm install patchly@latest to upgrade. Also works with pnpm, yarn, or bun.
The shortest Patchly setup loop is: capture runtime failures with the SDK, let Patchly open the GitHub issue, then use the repair note as your agent's starting context.
import { init } from "patchly";
const patchly = init({
dsn: process.env.PATCHLY_DSN, // required — from patchly.cc → Settings
release: process.env.VERCEL_GIT_COMMIT_SHA, // optional — enables source map resolution
userId: "user_123", // optional — track affected users
autoCapture: true, // default: true — captures uncaught errors
});
// manual capture
patchly.capture(new Error("something broke"));
await patchly.flush();
// manual breadcrumb
patchly.addBreadcrumb({ category: "custom", message: "User clicked checkout" });
// flush on shutdown (Node.js)
await patchly.close();When you create a project in the Patchly dashboard, you get two things from the Settings page:
Go to patchly.cc → Dashboard → open your project → Settings tab → API keys section. Click Generate new key to create one, then copy it immediately (it won't be shown again).
Your DSN embeds the API key and project ID in a single URL:
https://<API_KEY>@api.patchly.cc/<PROJECT_ID>The <API_KEY> portion (the psk_... string) is the same API key shown in Settings. You can use it in two ways:
init({ dsn}) for the SDKpsk_... key to the build plugin's authToken option for source map uploads# .env.local (or .env)
# SDK — full DSN URL
PATCHLY_DSN=https://psk_abc123@api.patchly.cc/your_project_id
NEXT_PUBLIC_PATCHLY_DSN=https://psk_abc123@api.patchly.cc/your_project_id
# Build plugin — just the API key (the psk_... part from your DSN)
PATCHLY_AUTH_TOKEN=psk_abc123All three env vars use the same underlying API key — the DSN just wraps it in a URL. Never commit these to version control.
Create a shared Patchly instance so the SDK is initialized exactly once, even when modules are re-imported across server and client boundaries.
// lib/patchly.ts
import { init } from "patchly";
export const patchly = init({
dsn: process.env.NEXT_PUBLIC_PATCHLY_DSN,
release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
});Create at your project root:
import type { Instrumentation } from "next";
let patchly: ReturnType<typeof import("patchly").init> | null = null;
async function getPatchly() {
if (process.env.NEXT_RUNTIME !== "nodejs" || !process.env.PATCHLY_DSN) return null;
if (patchly) return patchly;
const { init } = await import("patchly");
patchly = init({
dsn: process.env.PATCHLY_DSN,
release: process.env.VERCEL_GIT_COMMIT_SHA,
});
return patchly;
}
export async function register() {
await getPatchly();
}
export const onRequestError: Instrumentation.onRequestError = async (error) => {
const client = await getPatchly();
if (!client) return;
client.capture(error);
await client.flush();
};For Next.js 15+, onRequestError is the important hook: Next catches route-handler, Server Action, and render errors before they become process-level uncaught exceptions.
"use client";
import { useEffect } from "react";
import { patchly } from "@/lib/patchly";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
useEffect(() => { patchly.capture(error); }, [error]);
return (
<div>
<h2>Something went wrong</h2>
<button onClick={reset}>Try again</button>
</div>
);
}# .env.local
PATCHLY_DSN=https://<key>@api.patchly.cc/<projectId>
NEXT_PUBLIC_PATCHLY_DSN=https://<key>@api.patchly.cc/<projectId>init() in multiple files is fine because the SDK returns the existing instance if already initialized.NEXT_PUBLIC_ env vars. Use PATCHLY_DSN (without prefix) in instrumentation.ts and server-only code.dsn is undefined — safe to import during static prerendering and CI builds.error.tsx files over <PatchlyErrorBoundary> — the framework already provides the error boundary.import { init } from "patchly";
const patchly = init({ dsn: process.env.PATCHLY_DSN });
// Uncaught exceptions are captured automatically.
// For manual capture:
try {
riskyOperation();
} catch (err) {
patchly.capture(err as Error);
await patchly.flush();
}
// Graceful shutdown
process.on("SIGTERM", async () => {
await patchly.close();
process.exit(0);
});For standalone React apps (not using Next.js App Router), Patchly ships a drop-in error boundary that captures render-time errors automatically.
import { PatchlyErrorBoundary } from "patchly/react";
function App() {
return (
<PatchlyErrorBoundary fallback={<p>Something went wrong</p>}>
<MyApp />
</PatchlyErrorBoundary>
);
}The fallback prop accepts a React node or a render function with access to the error and a reset callback:
<PatchlyErrorBoundary
fallback={({ error, reset }) => (
<div>
<p>Error: {error.message}</p>
<button onClick={reset}>Try again</button>
</div>
)}
onError={(error, errorInfo) => {
console.log("Caught by Patchly:", error.message);
}}
>
<MyApp />
</PatchlyErrorBoundary>Works with React 18 and React 19. The react and react-dom peer dependencies are optional — Node-only apps that don't import patchly/react won't need React installed.
Beyond uncaught exceptions and unhandled promise rejections, the SDK automatically captures two additional error sources. Both are enabled by default.
Many frameworks (React, Next.js, Express) log errors to console.error without throwing. The SDK monkey-patches console.error to capture these automatically. The original console.error is always called first, so your existing logging is unaffected.
Deduplication is built in — if an error is already captured via an uncaught exception handler, the same error logged to console.error won't produce a duplicate event.
init({
dsn: process.env.PATCHLY_DSN,
captureConsoleErrors: false, // disable if needed
});The SDK intercepts fetch() (browser + Node) and XMLHttpRequest (browser only) to capture failed requests — HTTP 4xx/5xx responses, network errors, and timeouts. Each captured event includes the HTTP method, URL, status code, and response time.
Requests to the Patchly ingest endpoint are automatically excluded to prevent infinite loops.
init({
dsn: process.env.PATCHLY_DSN,
captureNetworkErrors: false, // disable if needed
});Upload source maps so Patchly can show original file names and line numbers in stack traces. Use the @patchly/build-plugin package to automate uploads as part of your build.
npm install -D @patchly/build-pluginThe authToken in every example below is your API key (the psk_... string from Settings). Store it as PATCHLY_AUTH_TOKEN in your environment.
// webpack.config.js
const { PatchlyWebpackPlugin } = require("@patchly/build-plugin/webpack");
module.exports = {
plugins: [
new PatchlyWebpackPlugin({
authToken: process.env.PATCHLY_AUTH_TOKEN,
}),
],
};// vite.config.ts
import { patchlyVite } from "@patchly/build-plugin/vite";
export default defineConfig({
plugins: [
patchlyVite({
authToken: process.env.PATCHLY_AUTH_TOKEN,
}),
],
});// next.config.mjs
import { withPatchlySourceMaps } from "@patchly/build-plugin/turbopack";
const nextConfig = { /* your config */ };
export default withPatchlySourceMaps(nextConfig, {
authToken: process.env.PATCHLY_AUTH_TOKEN,
});The plugin auto-detects the release tag from CI environment variables (VERCEL_GIT_COMMIT_SHA, GITHUB_SHA, etc.) or falls back to git rev-parse HEAD. You can override it with the release option. By default, .map files are deleted from the build output after upload.
| Option | Type | Description |
|---|---|---|
| authToken | string | Required. Your API key (psk_...) from Settings. Use env var PATCHLY_AUTH_TOKEN. |
| release | string? | Release identifier. Auto-detected from CI env vars. |
| deleteSourceMaps | boolean? | Default true. Delete .map files after upload. |
| endpoint | string? | Ingest endpoint. Default: https://api.patchly.cc |
| silent | boolean? | Default false. Suppress all console output. |
When an error group reaches 3 events and Patchly generates an AI fix, a GitHub issue is automatically created on your project's configured repository. The issue includes the error title, event count, timestamps, and the AI fix suggestion. No manual action needed.
You can also file a GitHub issue manually from the error detail page using the Create GitHub Issue button. The issue includes the full error context: event count, affected users, stack trace, AI-generated fix, and affected files.
To use GitHub issues (automatic or manual), your project needs:
repo scope is required for private repos)Issues are created with the bug and patchly labels, and include a link back to your Patchly dashboard. Each error group can only have one linked issue — the button changes to "View Issue" after creation.
When an error group reaches 3 events, Patchly automatically fetches your source code from GitHub and generates a fix suggestion using an LLM. Fix suggestions include:
View and vote on fixes in the dashboard.
Configure Slack webhook or email alerts in Settings. Alerts fire when an error group exceeds your threshold count within a time window.
| Method | Description |
|---|---|
| init(config) | Initialize the SDK. Returns { capture, addBreadcrumb, close }. |
| capture(error) | Manually capture an Error instance. |
| addBreadcrumb(crumb) | Record a breadcrumb attached to the next captured error. |
| close() | Flush pending events and remove global handlers. |
| Option | Type | Description |
|---|---|---|
| dsn | string | Required. Your project DSN. |
| release | string? | App version or commit SHA. Enables source map resolution. |
| userId | string? | User identifier for affected-user tracking. |
| autoCapture | boolean? | Default true. Auto-capture uncaught errors. |
| captureConsoleErrors | boolean? | Default true. Capture errors logged to console.error. |
| captureNetworkErrors | boolean? | Default true. Capture failed fetch/XHR requests. |
| enableBreadcrumbs | boolean? | Default true. Record breadcrumbs leading up to errors. |
| maxBreadcrumbs | number? | Default 20. Max breadcrumbs retained in the circular buffer. |
Patchly ships an agent skills guide for Claude Code, Codex, Antigravity, and other agentic editors. Add it to your project so your agent follows the same capture, issue, repair workflow without guessing:
curl -o PATCHLY.md https://patchly.cc/SKILLS.mdRead the public guide at /docs/skills. Project-specific skills URLs with your DSN pre-filled are available from each project's settings page.