Error Tracker Installation Skill
Install and configure gh-issue-tracker — a lightweight error tracking package that creates GitHub Issues instead of sending to SaaS platforms.
Quick Reference
npm install gh-issue-tracker # or pnpm add / yarn add
Required env vars: GITHUB_TOKEN, GITHUB_REPO
Prerequisites
- GitHub Fine-Grained Personal Access Token with:
- Repository access: target repository only
- Permissions: Issues (read/write)
- Generate at: GitHub → Settings → Developer settings → Fine-grained tokens
- Node.js >= 20 (uses Web Crypto)
Step 1: Determine the Architecture
ASK THE USER which errors they need to capture. This determines the architecture:
Option A: Server-Only Errors
Best for: APIs, backend services, CLI tools, scripts, workers.
Server error → captureException() → GitHub Issues API
gh-issue-trackerruns server-side onlyGITHUB_TOKENstays in server environment- Simplest setup: just
init()+captureException()
Option B: Server + Client Errors (Recommended for web apps)
Best for: Next.js, React, or any web app with a server component.
Browser error → POST to proxy endpoint → captureException() → GitHub Issues API
Server error → captureException() directly → GitHub Issues API
gh-issue-trackerstill runs server-side only- Browser errors are reported via a proxy endpoint (API route)
GITHUB_TOKENNEVER reaches the browser- Error boundaries in React catch client-side errors and POST to the proxy
Security Guidance
The package is server-side only (token stays off the client) — it cannot be imported in browser code.
The token risk depends on your setup:
- Fine-grained PAT with Issues-only on a public repo: Low risk. Someone with the token can create/close issues — same as anyone can do via the GitHub UI on a public repo.
- Classic token with
reposcope or private repo: Higher risk. Use the proxy pattern to keep the token isolated.
Proxy pattern (recommended for private repos or maximum security):
- Error boundaries send error details (message, stack, URL) to a proxy endpoint
- The proxy validates the request and calls
captureException()server-side - The GitHub token lives only in the proxy's environment
Step 2: Install the package
npm install gh-issue-tracker
(Use the project's package manager: pnpm, yarn, or npm)
Step 3: Set environment variables
Add to .env (or .env.local for Next.js):
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
GITHUB_REPO=myorg/myapp
Ensure .env is in .gitignore. For production, set these via your hosting platform (Vercel, Railway, etc.).
For Option B (client + server), also set:
ALLOWED_ORIGINS=https://myapp.com,https://staging.myapp.com
Step 4: Framework-Specific Setup
Next.js App Router
Server-side initialization (both Option A and B)
Create instrumentation.ts at project root:
import { init as initErrorTracker, captureException, flush } from 'gh-issue-tracker'
export async function register() {
initErrorTracker({
githubToken: process.env['GITHUB_TOKEN'] ?? '',
githubRepo: process.env['GITHUB_REPO'] ?? '',
environment: process.env['NODE_ENV'] ?? 'development',
enabled: !!process.env['GITHUB_TOKEN'],
labels: ['my-app'],
})
}
export async function onRequestError(error: unknown) {
if (error instanceof Error) {
captureException(error, { tags: { source: 'onRequestError' } })
} else {
captureException(new Error(String(error)), { tags: { source: 'onRequestError' } })
}
await flush()
}
See https://github.com/zot24/gh-issue-tracker/tree/main/examples/nextjs-instrumentation/` for a complete file with inline comments.
Client error capture (Option B only)
Step 4a: Create the proxy endpoint
Create app/api/errors/capture/route.ts — this is the server-side endpoint that receives browser errors and forwards them to GitHub.
Copy from https://github.com/zot24/gh-issue-tracker/tree/main/examples/nextjs-error-proxy/route.ts`. Key security features:
- Origin allowlist (
ALLOWED_ORIGINSenv var) - IP-based rate limiting (5 req/hour per IP)
- Zod schema validation
- 10KB body size cap
- Returns
200 OKfor invalid requests (doesn't leak validation logic)
Step 4b: Create error boundaries
Create app/error.tsx and app/global-error.tsx — these are React components that catch runtime errors in the browser and POST to the proxy.
Copy from https://github.com/zot24/gh-issue-tracker/tree/main/examples/nextjs-error-boundaries/`. These components:
- Catch errors via Next.js error boundary mechanism
- POST
{ message, stack, digest, url, boundary }to/api/errors/capture - Display a user-friendly error page with "Try again" button
- Fire-and-forget (
.catch(() => {})) — error reporting failure doesn't affect UX
Express / Node.js (Option A)
import { init, captureException, flush } from 'gh-issue-tracker'
// At app startup
init({
githubToken: process.env['GITHUB_TOKEN']!,
githubRepo: process.env['GITHUB_REPO']!,
environment: process.env['NODE_ENV'] ?? 'development',
labels: ['my-app'],
})
// Error handler middleware (MUST be after all routes)
app.use((err, req, res, next) => {
captureException(err instanceof Error ? err : new Error(String(err)), {
requestUrl: req.originalUrl,
tags: { method: req.method },
})
flush().finally(() => res.status(500).json({ error: 'Internal server error' }))
})
See https://github.com/zot24/gh-issue-tracker/tree/main/examples/express-middleware/` for a complete example.
Generic Node.js (Option A — scripts, workers, etc.)
import { init, captureException, flush } from 'gh-issue-tracker'
init({
githubToken: process.env['GITHUB_TOKEN']!,
githubRepo: process.env['GITHUB_REPO']!,
})
try {
// your code
} catch (error) {
captureException(error instanceof Error ? error : new Error(String(error)))
await flush()
}
Step 5: Verify
- Start the application
- Trigger a test error (server-side or client-side depending on your architecture)
- Check the GitHub repository's Issues tab
- Look for an issue with the
error-reportlabel - Trigger the same error again — verify it adds a thumbs-up reaction, not a duplicate issue
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
githubToken |
string |
— | GitHub PAT with Issues permissions (required) |
githubRepo |
string |
— | Repository in owner/repo format (required) |
environment |
string |
"development" |
Environment name shown in issue body |
labels |
string[] |
[] |
Additional labels on every issue |
enabled |
boolean |
true |
Kill switch to disable tracking |
onError |
(err) => void |
console.error |
Called when GitHub API fails |
rateLimitPerMinute |
number |
10 |
Max new issues created per minute |
dedupeWindowMs |
number |
60000 |
Suppress same fingerprint within this window |
reopenClosed |
boolean |
true |
Reopen closed issues on error recurrence |
screenshotUpload |
"user-attachment" | "branch" |
"user-attachment" |
Screenshot storage. Default is GitHub user-attachments (gh --attach). "branch" is GHES / legacy proxy only. |
captureBugReport({ screenshot }) needs Issues write only. No Contents permission, no APP_BASE_URL, no proxy route.
Security Recommendations
- Use a fine-grained PAT scoped to Issues only on a single repo
- Don't prefix the token with
NEXT_PUBLIC_orVITE_(these expose to browser bundles) -
.envfiles are in.gitignore - If using a proxy, add origin allowlist + rate limiting
- For private repos, use the proxy pattern to isolate the token
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| No issues created | Token missing or enabled: false |
Check GITHUB_TOKEN is set |
| 403 from GitHub API | Token lacks permissions | Ensure Issues read/write on target repo |
| Duplicate issues | Different stack traces (line numbers changed) | The normalizer handles this — check if the error message itself varies |
| Rate limited | High error volume | Increase rateLimitPerMinute or fix the underlying errors |
| Token in browser bundle | Env var prefixed with NEXT_PUBLIC_ or VITE_ |
Remove the prefix — the package is server-side only and doesn't need client exposure |