← Back to Blog

Sentry Tells You When Your App Broke. Nark Tells You What Will Break Before You Deploy.

By Nark Team

Sentry catches the instance of a bug after it fires in production. Nark catches the class of bug in your TypeScript code before you deploy. They sit on opposite sides of the deploy boundary, they answer different questions, and the right answer for a serious app is to run both. To check what would break before Sentry ever sees it, run npx nark --tsconfig ./tsconfig.json against your project.

Quick Answer: Sentry is post-deploy error monitoring (runtime observability of bugs your users already hit). Nark is pre-deploy static analysis (TypeScript scan for missing error handling around npm packages like Stripe, Prisma, and openai). They are complementary, not competitive. Run npx nark --tsconfig ./tsconfig.json in CI or your shell to catch the class before deploy; run Sentry in production to catch any instance that slips through.


What does Sentry actually catch?

Sentry is the established leader in runtime observability for application code. As of 2026 the homepage tagline reads "Code breaks, fix it faster" and the surface spans error monitoring, performance and tracing, session replay, logs, and metrics, all stitched together by a unified trace timeline. The pricing tiers run from a free Developer plan (1 user, 5,000 errors per month) through Team ($26 per month on annual), Business ($80 per month on annual), and Enterprise. Sentry instruments your application at runtime, captures unhandled exceptions, groups them by stack signature, and surfaces them with full request context: stack trace, breadcrumbs, user, release, and environment.

The classic Sentry usage pattern looks like this:

import * as Sentry from '@sentry/node';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.1,
});

export async function chargeCustomer(amountCents: number) {
  try {
    const intent = await stripe.paymentIntents.create({
      amount: amountCents,
      currency: 'usd',
    });
    return intent;
  } catch (error) {
    Sentry.captureException(error);
    throw error;
  }
}

When that route is called in production and the Stripe call throws, Sentry receives the exception, attaches the request context, groups it with prior occurrences, and alerts the on-call engineer. The team learns the bug exists, sees how often it fires, and ships a fix. That feedback loop is genuinely valuable and Sentry has spent a decade making it fast, reliable, and polyglot. JavaScript and TypeScript, Python, Java, Go, Ruby, .NET, Rust, and PHP are all first-class citizens.

The keyword in that paragraph is runtime. Sentry has to see the bug fire to know it exists.


Why doesn't Sentry catch missing error handling before deploy?

Because Sentry is an observability product. Its model of your application is "errors observed during execution," and to observe an error, something has to execute the code, hit the failing path, and throw. A try/catch that is missing from your codebase produces zero data in Sentry until a real user trips the failing path in production. At that point Sentry does its job, captures the exception, alerts you, and helps you ship a fix, but the user already hit the broken page.

This is not a flaw in Sentry. It is the design. Sentry sits on the right side of the deploy boundary on purpose: production is where the truth lives, and the truth includes inputs and conditions you could never have anticipated from the code alone. The trade-off is that you only learn about a missing handler after it has already caused harm.

Three categories of bug are particularly bad fits for runtime-only detection:

  • Long-tail failure modes that production traffic rarely exercises. A try/catch missing around stripe.paymentIntents.create() only matters when a card is declined, which might happen 0.5% of the time. The first declined card might be a week after launch.
  • Failures concentrated in a specific code path that test traffic skips. A Prisma query that throws PrismaClientKnownRequestError with code P2002 on duplicate-email signups will pass every staging test that uses fresh emails and only fire when a real user re-registers.
  • Cost-sensitive errors that should never reach production at all. An openai.chat.completions.create() call that swallows HTTP 429 rate limits will quietly burn through retries against your billing while users see hangs. Sentry sees the 429 once it fires; the bill arrives anyway.

For these, by the time Sentry tells you something is wrong, the user has already seen the broken page and your incident timeline already has a new entry. The class of bug existed in the code the moment it was committed. Static analysis can read the code and flag it without ever running it.


What does Seer AI Code Review do, and what doesn't it do?

In 2026 Sentry shipped Seer, an AI debugger add-on that runs across all tiers. Seer has three published capabilities: Root Cause Analysis, Fix Generation, and AI Code Review. The Code Review feature is the one closest to a pre-deploy check, and it deserves a careful read because the framing matters.

The marketing language from sentry.io/product/seer reads: "Catch tomorrow's bugs in today's PR. Seer reviews your PRs against real production data, the errors, traces, and failure patterns Sentry has already seen from your app." Seer sends suggested fixes to Claude, Copilot, or Cursor for the human to apply.

That is a powerful feature, and the framing is honest about its scope. The phrase that does the work is "real production data." Seer's input is errors Sentry has already observed in your application. It is an LLM-based review that knows what your app has thrown in the past and uses that history to spot patterns in your PR. If your app crashed last month because a Prisma query was missing a P2002 handler, Seer notices when a new PR introduces a similar unhandled pattern and flags it.

What Seer is not, by design:

  • It is not deterministic static analysis. Seer reviews PRs with an LLM informed by your production error history. Two runs on the same PR can produce different findings. Static analysis like Nark reads the AST and produces the same finding on the same code every time.
  • It does not work on day-zero apps with no production data. A green-field TypeScript project on its first deploy has zero error history for Seer to learn from. Seer needs Sentry to have observed something first; the value compounds as your app accumulates incidents.
  • It does not encode the documented failure modes of npm packages you have not yet hit. If your team has never been bitten by a Stripe StripeAPIError, Seer has no signal to look for it. Nark already knows Stripe documents that class because the corpus encodes it.

Seer is a real improvement in pre-deploy coverage for teams already running Sentry in production. It is not a replacement for static analysis, and Sentry does not market it as one. The two layers are complementary.


How are Sentry and Nark different?

The clearest way to see the difference is to put them in a table by dimension. Both products have legitimate claims to "pre-deploy" relevance in 2026, but the underlying methods, inputs, and coverage are different enough that they answer different questions.

DimensionSentry (plus Seer AI Code Review)Nark
Primary roleRuntime observability of errors your users hitStatic analysis of TypeScript code against documented package contracts
Required stateLive deployment with traffic, or in Seer's case, accumulated production error historyDay-zero TypeScript code; zero runtime required
Input sourceErrors and traces Sentry observed in your appTypeScript AST plus the curated Nark Profile corpus (StripeCardError, PrismaClientKnownRequestError, OpenAI.APIError, and 160+ more)
MethodRuntime exception capture; LLM-based PR review in SeerDeterministic static analysis; same code produces the same findings every run
Language coveragePolyglot (JS/TS, Python, Java, Go, Ruby, .NET, Rust, PHP)TypeScript only
Package knowledgeGeneric; learns what YOUR app has thrownSpecific; knows what every supported package CAN throw per its documentation
Best for catchingRecurrence of bugs you have seen before, and surprise failure modes only production revealsThe class of package-error bug before it ever fires in production
When it runsAfter deploy (Sentry); on the PR using prior production data (Seer)Before deploy, in CI or shell
PricingFree Developer (1 user) -> $26/mo Team -> $80/mo Business -> Enterprise. Seer is a subscription add-on across all tiers.Nark CLI free (AGPL-3.0); public Profile corpus free (CC-BY-4.0); paid nark-corpus-pro tier for extended coverage

The point of the table is not "which is better." The point is that they are answering different questions and the right answer for any serious application is to run both.


How does the pre-deploy plus post-deploy stack fit together?

The deploy boundary is the cleanest way to draw the line. Nark runs before the code ships. Sentry runs after. Seer AI Code Review threads the needle by running on the PR but feeding from post-deploy data, which is why it complements rather than replaces either side.

Here is the lifecycle of a single TypeScript service in a team that uses both:

Day 0 code      npx nark            CI gate          Deploy        Sentry              Seer AI Code Review
(no runtime)     scans AST          blocks merge                  captures              reviews next PR
                 against corpus     on new                        runtime               using prod error
                                    violations                    exceptions            history
   |                |                  |              |              |                       |
   v                v                  v              v              v                       v
 author        static analysis     gatekeeping     production    observability        PR-time review
 writes code   catches the         on new gaps     traffic       catches              informed by
               class of bug                        hits the      instances of         prior incidents
               before any                          code          escaped bugs
               runtime

Three concrete handoffs make the layering work:

  1. Nark catches what the package documentation already tells you. Stripe documents StripeCardError. Prisma documents P2002. openai documents 429s. These are facts that exist in the package docs the moment you import the library. Nark encodes them in the Profile corpus and flags every awaited call that does not branch on them. You do not need to wait for a real card decline to find out you forgot the handler.

  2. Sentry catches what static analysis cannot predict. A pg connection that drops mid-transaction because your database failed over. A third-party API that started returning malformed JSON yesterday. A race condition that only fires under load. Static analysis cannot see those because the bug is in the runtime conditions, not in the code structure. Sentry sees them the moment they fire.

  3. Seer closes the loop between the two. Once your app has accumulated production error history, Seer's PR review catches recurrence of bugs you have already hit. If your team shipped a missing P2002 handler last month and now a new PR introduces a similar pattern, Seer flags it before merge. The signal it uses is your own production error history, which is the one signal Nark does not have.

The cleanest mental model: Nark covers what the documentation tells you will happen. Sentry covers what production tells you happened. Seer covers what production told you last month, applied to the PR you are reviewing today.


When should I use Nark vs Sentry?

Both, on the same project. They do not compete on capability, they layer on lifecycle.

That said, if you are early in either direction and trying to pick the next investment, the practical sequencing usually looks like this:

  • You already run Sentry and you ship features fast. Add Nark next. Sentry is telling you about bugs after they fire; Nark adds a CI gate that catches the class of bug before deploy. This is the most common Sentry-plus-Nark customer profile and the layering pays off within the first sprint.
  • You are pre-launch on a green-field TypeScript app. Start with Nark. You have no production traffic for Sentry to observe yet, and you have no error history for Seer to learn from. Nark works on day zero against your AST. Add Sentry the day you put traffic on the deploy.
  • You ship a polyglot stack and TypeScript is one piece. Sentry is the natural foundation because it covers all your languages. Layer Nark on the TypeScript services where you have the highest customer impact from silent package failures (anything that touches Stripe, Prisma, openai, payment processors, AI APIs).
  • You are on Sentry's Developer free tier and you only have one user. Nark CLI is free and AGPL-3.0; the public corpus is free and CC-BY-4.0. Both lower-the-floor tools deserve a spot in your workflow before you have to spend on either.

The honest framing: Sentry is the established leader in runtime observability and there is no good reason not to use it. Nark is the deterministic static-analysis layer that catches package-error gaps Sentry will never see until a real user hits them. Use both.


What does the Nark scan look like in practice?

The same Stripe route used as the Sentry example earlier, but seen through Nark before any traffic ever hits it. The wrong-way version (missing the try/catch) is what most AI code generators emit and what most rushed PRs miss:

// Wrong way: nark will flag this
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function chargeCustomer(amountCents: number) {
  const intent = await stripe.paymentIntents.create({
    amount: amountCents,
    currency: 'usd',
  });
  return intent;
}

Running npx nark --tsconfig ./tsconfig.json produces a violation along the lines of:

src/checkout.ts:6  ERROR  nark/stripe/missing-try-catch
  Awaited call to stripe.paymentIntents.create() is not wrapped in a try/catch.
  Stripe documents at least 7 error classes for this method including
  StripeCardError, StripeAPIError, and StripeRateLimitError.
  Profile: stripe@^17.0.0

The right-way version that branches on the documented Stripe error classes:

// Right way: nark passes; ready to also instrument with Sentry
import Stripe from 'stripe';
import * as Sentry from '@sentry/node';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function chargeCustomer(amountCents: number) {
  try {
    const intent = await stripe.paymentIntents.create({
      amount: amountCents,
      currency: 'usd',
    });
    return intent;
  } catch (error) {
    if (error instanceof Stripe.errors.StripeCardError) {
      // Card was declined. Tell the user, do not retry.
      throw new Error(`Card declined: ${error.message}`);
    }
    if (error instanceof Stripe.errors.StripeAPIError) {
      // Transient Stripe outage. Safe to retry with backoff.
      Sentry.captureException(error);
      throw new Error(`Stripe transient error: ${error.message}`);
    }
    Sentry.captureException(error);
    throw error;
  }
}

That is the layered version. Nark caught the missing try/catch before deploy and forced the author to think about which Stripe error classes to branch on. Sentry now instruments the route so that any exception escaping the catch is captured in production. The same code is protected on both sides of the deploy boundary.

This is also a good example of where Replit Agent output typically lands when generated without explicit error-handling instructions: the wrong-way version. Nark scans the AST and flags it; Sentry would have caught it eventually, in production, after a user hit it.


Does Nark replace anything in my Sentry workflow?

No. Nark does not capture runtime exceptions, group errors, alert on regressions, replay sessions, trace requests, or do anything Sentry does after the code is live. If you turn off Sentry and only run Nark, you lose all of your production visibility.

What Nark replaces is the implicit assumption that "we will catch missing handlers in code review" or "we will write a test for that one." Both of those break down at any meaningful team velocity. Nark adds the gate that always runs, never gets skipped, and never misses a handler the package documentation explicitly tells you is needed.

The simplest way to say it: Nark is a build-time check; Sentry is a runtime instrument. They have no overlap on what they output. They have a lot of overlap on what they protect you from.


Frequently Asked Questions

Is Nark a Sentry alternative?

No. Sentry is a runtime observability product; Nark is a pre-deploy static analyzer. They sit on opposite sides of the deploy boundary and answer different questions. Sentry tells you about errors your users already hit. Nark tells you which package-error classes your TypeScript code is missing handlers for, before any user hits anything. The right setup for a serious app is to run both. If you only run one, run Sentry first if you have production traffic and no observability today; run Nark first if you are pre-launch on a green-field TypeScript app with no traffic to observe yet.

Is Nark shift-left error monitoring?

Yes. Nark is the shift-left counterpart to runtime error monitoring tools like Sentry. "Shift-left" means moving error detection earlier in the lifecycle, from production observation back into the developer's editor, CI pipeline, or pre-deploy gate. Nark shifts the detection of missing-handler bugs from "Sentry told us in production at 3am" all the way left to "CI blocked the merge before deploy." The package-contract corpus is what makes the shift-left work: instead of needing to first observe the bug in production (Sentry's model) or accumulate enough production history for an LLM to spot patterns (Seer's model), Nark reads the documented Stripe, Prisma, and openai failure modes from the Profile corpus and flags missing handlers the first time you write the code.

Does Sentry Seer replace static analysis?

No, and Sentry does not market it that way. Seer AI Code Review is an LLM-based PR reviewer that uses your accumulated production error history to spot patterns. That is a real improvement in pre-deploy coverage and worth turning on if you already pay for Sentry. It is not deterministic static analysis. Two Seer runs on the same PR can produce different findings, and Seer cannot flag a missing handler for a failure mode your app has never hit. Nark reads the AST against curated package contracts and produces the same finding every time, including for failure modes your app has never seen. Use Seer for "what production has taught us"; use Nark for "what the documentation already tells us."

Can I use both Sentry and Nark on the same project?

Yes, and you should. They are explicitly complementary. Nark runs in CI or your shell before deploy and gates merges on new violations. Sentry runs in production and captures exceptions that fire. The combined coverage is broader than either alone: Nark catches the documented failure modes before they fire, Sentry catches the surprise failure modes that only production reveals, and if you have Seer turned on it bridges the two by reviewing PRs against prior production error history.

Do I need Sentry if I already run Nark?

Yes, for any app with real production traffic. Nark catches the class of bug before deploy, but no static analyzer can predict every runtime condition: a database failover mid-transaction, a third-party API that started returning malformed JSON yesterday, a race condition that only fires under load, or a memory leak that only shows up after 12 hours of uptime. Sentry sees those the moment they fire and gives you the stack trace, request context, and grouping you need to ship a fix. The honest sequencing: if you are pre-launch with no traffic, Nark first; if you are post-launch with real users, you need both. Running Nark does not replace the need for runtime observability any more than running unit tests replaces the need for integration tests.

What does Nark catch that Sentry can't?

Nark catches missing error handling for failure modes your app has not yet hit. Sentry, by design, only sees errors that have actually fired in your runtime. If your app has never had a card declined, Sentry has no signal about your missing StripeCardError handler. If your app has never had a duplicate-email signup, Sentry has no signal about your missing PrismaClientKnownRequestError code P2002 handler. Nark already knows those error classes exist because the Profile corpus encodes the Stripe and Prisma docs. It flags the missing handler the first time you write the code, not the first time a user trips it.

How is Nark different from ESLint or Semgrep?

ESLint is a generic JavaScript linter. Its rules are about code style, common bugs, and language-level patterns (no-unused-vars, prefer-const, no-await-in-loop). It does not know what Stripe documents as throwable, what Prisma's error codes mean, or what openai status codes require which retry strategy. Semgrep is a pattern-matching engine across many languages; you can write custom Semgrep rules for individual package patterns, but you have to author and maintain them yourself. Nark ships 165+ curated TypeScript-specific package Profiles encoding documented error classes for the npm libraries production apps actually depend on. The wedge is "deterministic static analysis against curated package contracts," and ESLint and Semgrep do not occupy that slot.

What npm packages does Nark check?

Nark ships 165+ Profiles covering the npm libraries most likely to fail silently in production: stripe, prisma, openai, @anthropic-ai/sdk, axios, pg, redis, mongoose, @aws-sdk/client-s3, twilio, sendgrid, resend, ioredis, and many more. Each Profile encodes the package's documented error classes and the postconditions that catch the most commonly missed handlers (e.g. PrismaClientKnownRequestError P2002 for duplicates, StripeCardError for declined cards, OpenAI.APIError status 429 for rate limits).


Try It Now

npx nark --tsconfig ./tsconfig.json

Nark scans 165+ npm packages for missing error handling, including stripe, prisma, openai, axios, and pg. It runs in any TypeScript project; pair it with Sentry for the layered pre-deploy plus post-deploy coverage that serious apps need. To produce a JSON report you can hand to Claude, Copilot, or Cursor for in-place fixes:

npx nark --output nark-report.json

Then in your AI assistant: "read nark-report.json and fix every violation it lists."