PostHog vs TrackJS: A Hands-On Error Tracking Comparison for 2026

Stanley Ulili
Updated on November 4, 2026

The bad release in my test introduced one new bug: a payment gateway error that fired on every fifth card charge. PostHog turned it into two issues, and TrackJS turned three failed charges into three separate errors, plus a fourth copy from the browser. The two tools group errors in opposite ways, and on this release each way broke down.

TrackJS groups by the error message, and the charge amount was part of the message. PostHog groups by the stack trace, which ignores the amount. On Node, though, PostHog also counted the runtime's internal frames, and the slow database query in that release changed them. Which of these failure modes you'd rather live with says a lot about which tool fits your team.

PostHog is a product platform where error tracking is one product among many, sharing a project and its user data with session replay, logs, tracing, metrics and analytics. An error in PostHog can open the session recording, the server trace and the logs that go with it.

TrackJS keeps to one job. TrackJS is a browser-first JavaScript error monitor, paired with a lighter agent for Node.js and an AI Debugger on every error, and its bill is set by page views. Each error carries a telemetry timeline of the clicks, requests, navigations and console lines that came before it.

I ran both against the same intentionally broken test bookstore in late October 2026. You'll see them compared on setup, browser and server errors, source maps, debugging, the path from browser to server, grouping, replay, releases and regressions, performance, logs, alerts, AI and pricing.

PostHog vs TrackJS: Results at a glance

My own Express and Vite setup produced these verdicts. Public source maps, a framework that PostHog's wizard supports, or alerts wired to a channel could move a few.

Area Better in my test Why
Setup PostHog More switches, all documented; TrackJS's own Express handler hung 5xx responses
Browser errors Tie Each one received all the storefront crashes once configured
Server errors PostHog Source lines read from disk and a trace per error; TrackJS shows a file and line only
Source maps PostHog Private upload at build time; TrackJS needs public files or a drop per error
Debugging an error PostHog Code, recording, timeline and trace on one issue
Browser to server TrackJS Each fetch in its timeline opens the matching server exception
Error grouping PostHog, narrowly Both split the payment bug; TrackJS also split by IDs and quantities
Session replay PostHog Replay is built in; TrackJS has none
Releases and regressions PostHog Reopened the returning bug; TrackJS left it marked Fixed
Performance PostHog Found the slow query by version; TrackJS has no performance data
Logs PostHog A logs product linked to traces; TrackJS keeps console lines as telemetry
Alerts Not compared No delivery channel was set up in either
AI-assisted debugging TrackJS Read the bad release more carefully, with no code available to either
Price PostHog A free tier with 100,000 exceptions a month; TrackJS has no free plan

How I tested PostHog and TrackJS

The test app is a small online bookstore built to fail. It has an Express API backed by SQLite, a Vite storefront with hash routes, a fake price service and a background job that builds a sales report. Both tools received the same planted failures from the same code, driven by scripted shoppers in a headless browser.

The planted problems were:

  • Browser crashes: a wishlist button that reads from a missing object, and failed API calls the storefront reports itself.
  • Server crashes: a checkout that throws on an unknown coupon, a book page that reads a missing record, and an out-of-stock error.
  • A flaky dependency: the price service returns 503 now and then.
  • Async failures: an unhandled rejection from a recommendations call, and a report job that throws.
  • A slow query and a new bug: turned on only in the third release.

The releases

Release What I tested
1.0.0 Installing both, first errors, source maps, error pages
1.1.0 Regression detection: a resolved bug shipped again with no fix
1.2.0 The bad deploy: checkout's database lookup slows down and payments start failing

The 1.2.0 release mattered most. A real bad deploy rarely fails loudly, and this one mixed a slowdown with a new error on the same code path, which is the kind of release you most need a tool to explain.

SDKs and test limitations

Platform PostHog TrackJS
Server posthog-node 5.57.2, plus OpenTelemetry for traces, logs and metrics trackjs-node 1.3.0
Browser posthog-js 1.441.0 trackjs 3.10.4
Plan Free, US Cloud Trial priced by page views
  • Scope: TrackJS sells browser error monitoring with a Node add-on. It doesn't claim tracing, logs, replay or performance, so I judged it on errors first and treat those gaps as outside its scope.
  • Counts: each tool saw its own traffic runs, so error and event counts aren't comparable one to one.
  • Local source maps: the storefront ran on localhost, which rules out TrackJS's automatic source maps.
  • Headless shoppers: PostHog's browser SDK ignores headless browsers by default, so I turned that filter off for the test.
  • No repository connected: neither AI assistant could read the code.
  • Alerts: I didn't connect Slack, email or any other channel to either tool.

Setup and onboarding

Setup shows how much of a tool you have to configure before it tells you anything useful. PostHog asks you to choose products and settings. TrackJS asks for a token and one handler.

PostHog: More products, more switches

You start PostHog by naming a goal. Mine was "Find and fix issues", and PostHog ticked a few products in response:

PostHog onboarding with three products pre-selected

Error Tracking isn't among them. I added it, with Logs and Metrics, from "Show all products".

The install page leads with an AI wizard that has no Express option, so I used the manual Node.js page:

PostHog Node.js setup page with the Express note

The note about setupExpressErrorHandler matters, because Express catches errors before the SDK can see them. The install I had was too old for that helper, and npm install posthog-node@latest fixed it.

Three more switches decided what I saw:

  • Headless browsers are dropped by posthog-js unless you set opt_out_useragent_filter: true, which only matters for test traffic like mine.
  • Replay stayed switched off in project settings, though onboarding had it ticked.
  • Exception autocapture for the web is a project setting, or the capture_exceptions: true option in code.

"Installation complete" appeared as soon as the server sent its first event. The Logs step got stuck behind a loading overlay until I closed it.

TrackJS: A token, and one handler to work around

TrackJS starts with a script tag and its token, then a four-step wizard: Agent, Applications, Team and Ignore.

TrackJS install page with the script-tag snippet

The browser side worked on the first try. The application form was less forgiving:

TrackJS application form with a doubled key

The key fills itself from the name as you type, so my own typing doubled it. Keys can't be edited afterwards, only renamed, so the fix was a new application.

The server side had a real bug. In trackjs-node 1.3.0, TrackJS.Handlers.expressErrorHandler() ignores the options you pass it, reports the error and never calls next(). Following the documented Express setup, every request that hit a 5xx error hung with no response. I reproduced it on a minimal Express 5 server, then worked around it in the test adapter by passing the error on myself.

Setup comparison

Capability PostHog TrackJS
Install path AI wizard or manual SDK pages Script tag, npm module or legacy snippet
Express support ✔: setupExpressErrorHandler, current package required ✘: documented handler hangs 5xx requests in 1.3.0
Defaults to change Error Tracking, replay, headless filter, web autocapture None for the browser
Server and browser One project One application, two agents
Rough edges Logs step stuck behind an overlay Keys can't be edited; /settings opens an error page

Verdict: PostHog. It took more switches to get going, but each one was documented. TrackJS's server handler broke the app's error responses until I patched around it, and anyone who follows its docs on Express hits that.

Error capture

Capture is the minimum: every planted failure should arrive, once, with enough context to tell where it came from.

PostHog: Every browser error once the filter was off

With the headless filter switched off, PostHog's issues list held every browser failure: the wishlist crash, the storefront's own API errors and the failed tracking request.

PostHog issues list after the first clean run

Each row has occurrences, sessions and users, with status, severity and assignee you can set inline.

On the server, it caught the coupon, book, out-of-stock, price service and report job errors. Two details stood out:

  • The unhandled promise rejection never arrived. My reading is that posthog-node waits for Node to rethrow such rejections, and the app's own rejection handler stops that from happening.
  • Anonymous requests count as new users. Each request without a signed-in user got a fresh ID, so the price service error showed 24 users for 24 events.

TrackJS: Every failure, and some of them twice

TrackJS received every planted failure, including the unhandled rejection and the report job. After a clean run, it showed 99 errors in 29 groups from 4 users.

It also adds automatic network errors next to the app's own reports:

TrackJS errors list with a checkout failure reported twice

The checkout failure appears once as the storefront's own error and once as TrackJS's 500 : POST /checkout. Expect double rows for every failed API call unless you turn the network capture off.

TrackJS also turns console.error into error reports by default. A "Cannot set headers after they are sent" line became its own group, which other tools I've tested only logged.

Capture comparison

Capability PostHog TrackJS
Browser crashes ✔: after turning off the headless filter ✔: by default
Server crashes ✔: all request errors ✔: all request errors
Unhandled rejection ✘: missed in my setup ✔: captured
Background job ✔: captured ✔: captured
Automatic network errors ✘: the app's own reports only ✔: added beside the app's reports
Console errors as reports ✘: not by default ✔: on by default
User counts Anonymous requests inflate users Per signed-in user

Verdict: Tie. TrackJS caught more by default, including the rejection PostHog missed. PostHog's rows carried more per issue, and it reported each failure once.

Debugging an error

Once an error lands, the question is how quickly the page tells you which line failed and what the user was doing.

PostHog: Code, recording and trace on one issue

On the server, posthog-node reads source files from disk, so the coupon error showed the failing line with its neighbours:

PostHog coupon error with server source lines highlighted

Line 150 is highlighted, and "Show 1 vendor frame" hides the Node internals. No repository link was needed.

Browser errors get a Timeline tab with the session's pageviews, exceptions, custom events and logs:

PostHog Timeline tab for a browser error

Once replay was on, the Recording tab played the moment of the crash:

PostHog Recording tab at the moment of the wishlist error

The cursor is on "Add to wishlist", and the page shows who was signed in. At the Normal privacy setting only inputs are masked, so page text stays readable.

The Properties tab had the URL, library version and my custom fields, but no request body.

TrackJS: A transcript of the visit

A TrackJS error opens on its telemetry timeline:

TrackJS browser error with its telemetry timeline

Clicks come with the element's HTML, fetches with method, status and duration, and hash navigations in order. With "Errors on Page" you can jump between the errors one page load produced, and General Information adds Time On Page and the viewport.

Server errors are thinner. The coupon error showed server.js 150:45 with no code lines, no request body and no headers. Its timeline also mixed this request's breadcrumbs with console lines from minutes earlier, such as the app's startup messages. The Node agent added the machine's hostname, username and working directory to every server error by default.

Debugging comparison

Capability PostHog TrackJS
Server code lines ✔: read from disk ✘: file and line only
Browser code lines ✔: after a source map upload ✔: after a manual map drop
Activity before the error Timeline of events and logs Telemetry of clicks, fetches and console
Session recording ✔: on the issue ✘: not offered
Request body ✘: not captured ✘: not captured
Per-request isolation ✔: events tied to the session Partly: console history leaks in

Verdict: PostHog. Its issues had the code, the recording and the trace. TrackJS's timeline is the clearest single view of what a browser did, but its server errors stop at a line number.

Source maps

A minified stack is useless in production, so how a tool gets your source maps decides whether browser errors are readable at all.

PostHog: Private upload, and a new issue for each mapped error

PostHog's onboarding offers three options: unminified code, public maps it fetches, or private upload at build time. I used posthog-cli sourcemap inject and upload, and the wishlist frame resolved:

PostHog wishlist error with source-mapped code

The frame now reads ../../src/pages.js@121:13, with line 122 highlighted.

The upload had a side effect:

PostHog issues list with the wishlist error twice

PostHog builds the fingerprint from the resolved frames, so the minified and mapped versions of the same crash became two issues. You'd see this once per bug when you first add maps.

TrackJS: Public maps, or a drop that doesn't stick

TrackJS fetches source maps from your script's public URL. On localhost, the stack said so:

TrackJS source map information for a localhost script

Dropping the map file on the stack did the job:

TrackJS wishlist error after dropping a source map

Sourcemap Support switched to ON, and the frame read ../../src/pages.js 122:13. TrackJS applies the map in your browser only, so after a reload the same error was minified again. For maps to stick, the files must sit on a public URL, which you can limit to TrackJS's IP addresses or a secret header.

Source map comparison

Capability PostHog TrackJS
Private upload ✔: CLI at build time ✘: not offered
Public fetch ✔: offered ✔: the automatic mode
Manual map ✘: not needed ✔: per error, not saved
Side effects Mapped errors open new issues None

Verdict: PostHog. Its private upload fits a normal build pipeline. TrackJS works well once maps are public, but private builds get only a manual drop that you repeat on every error.

From the browser to the server

When a browser request fails, you want to reach what the server did with it. Neither tool sells distributed tracing for the browser, and each found its own way across.

PostHog: A server trace from the error, a session from the browser

Each PostHog server error has "View trace", which opened the coupon request's trace:

PostHog trace for the failing checkout request

It shows the request at 2.67 ms, eight spans, the Express middleware and my custom SELECT book for checkout span. What it lacks is the browser half: the storefront SDK records no spans, so the trace opens at the API. The two sides connect through the session ID.

The Node agent stamps responses with a correlation header that the browser agent stores, which puts a link beside each fetch:

TrackJS checkout fetch with the server errors link

On the failed checkout, that link led to the server's coupon exception for the same request. It appears on successful fetches too, and on a 404 with nothing behind it, it led to a page saying the error doesn't exist.

Browser-to-server comparison

Capability PostHog TrackJS
From a server error ✔: View trace ✘: no trace
From a browser request Through the session ID ✔: link to the server error
Browser spans ✘: not created ✘: not created
Server timing ✔: spans with durations ✘: not recorded

Verdict: TrackJS. From the browser side, it went straight from a failed fetch to the exception it caused. PostHog's trace is better evidence once you're on the server, but it doesn't start at the click.

PostHog's trace began at the server, and TrackJS's link landed on an error without the work that led to it. I also tested the same storefront visit in Better Stack, which drew the browser and the API on one timeline:

Better Stack timeline joining storefront and API spans

Each click and fetch sits above the server spans it triggered, so a slow or failing step is visible without switching tools.

See a visit from the click to the API

Error grouping

Grouping decides whether one bug is one row. Too loose and a new bug hides in an old one. Too strict and one bug fills the list.

PostHog: Grouped by stack, split by Node's internals

PostHog ignores the message and groups by stack frames, so different charge amounts didn't split the payment error. On 1.2.0, it still became two issues:

PostHog payment error issue without vendor frames

Both issues have the same two lines from my code. One also includes Node's internal timer frames, hidden behind "Show 2 vendor frames" yet still part of the fingerprint. The slow query changed the async path, so the same throw produced two different stacks.

That release split older bugs too. The coupon error gained two sibling issues, and the out-of-stock error gained two, while errors whose async path didn't change kept their single issue.

The fix is in Configuration:

PostHog grouping rules page with no rules

Grouping rules match on exception properties, first match wins. You can also merge issues by hand from the Fingerprints panel. I didn't create a rule.

TrackJS: Grouped by message, split by every value in it

TrackJS groups by message text, so the payment amount decided the group:

TrackJS search with one payment group per amount

Three failed charges became three server groups with one event each, plus a browser copy. The same rule split earlier errors by book title, quantity and ID:

TrackJS errors list with message variants in separate groups

Automatic URL grouping rewrites the URLs TrackJS records itself, such as 500 : GET /books/${books}, but not URLs inside the app's own messages. Each tracking request with a different ID list became its own group, six in all. "Create Group" and Settings, Error Groupings can correct this. I didn't test them.

Grouping comparison

Capability PostHog TrackJS
Grouping key Stack frames Message text
Payment bug on 1.2.0 2 issues 3 server groups and 1 browser group
Values in messages ✔: ignored ✘: each value splits
Timing changes in Node ✘: new issues ✔: no effect
Correction tools Grouping rules, merge by hand Create Group, Error Groupings, not tested

Verdict: PostHog, narrowly. Both broke one bug into several rows on the release that mattered. PostHog's split came from a timing change and needed a rule. TrackJS splits on every value a message carries, so the list grows with your data.

PostHog split the payment bug on its stack, and TrackJS split it on every amount. In my Better Stack test, the same release produced one payment group:

Better Stack single payment error group with its source lines

All the amounts sit in that group, with the throwing lines under the stack, and the coupon bug kept one group across three releases. After the deploy, the list grew by a single row.

Get one row per new bug

Session replay and real user monitoring

Replay lets you watch what a user did, so you skip piecing it together from events. It's the clearest product difference between these two.

PostHog: Replay in the same project, off until you turn it on

Session replay is part of PostHog, but it was off at the project level until I enabled it:

PostHog project settings with session replay off

Until then, every error said "Replay was not active". Console logs and network timings were already on, and I added header and body capture, Web Vitals autocapture and dead-click autocapture.

After that, the replay home listed recent sessions:

PostHog session replay home with recent recordings

It showed "15 recently active recordings", with a "Session highlights" scanner that uses AI credits and "Summarize this recording" under the player. I didn't test either, or the Web Vitals reports.

TrackJS: A timeline with no recording

TrackJS has no replay. Its timeline records clicks, inputs with masked values, navigations, fetches and console lines, which covers much of what you'd watch a recording for. It doesn't show you the page.

Replay comparison

Capability PostHog TrackJS
Session replay ✔: off by default at the project level ✘: not offered
Replay on the error ✔: Recording tab ✘: not offered
Text masking by default Inputs only Input values masked in telemetry
Web Vitals Included, not tested ✘: not offered

Verdict: PostHog. It records the session and links it from the error. TrackJS's timeline is a good substitute for many bugs, but it isn't a recording.

Release tracking and regressions

The release test was simple. I resolved the coupon bug on 1.0.0 and left its code untouched for 1.1.0.

PostHog: Reopened, with no regressed label

PostHog's status menu has Active, Resolved and Suppressed, with no option to resolve in a future version. Resolving showed a toast I hadn't seen in an error tracker before:

PostHog toast offering to tell affected users the bug is fixed

"Tell them it's fixed" would email the two signed-in users who hit the bug. I didn't send it.

On 1.1.0, PostHog reopened the issue by itself:

PostHog coupon issue back to Active on 1.1.0

It went back to Active, from 44 to 64 occurrences. There's no "regressed" label, so in the list it looks like any other open issue.

The Breakdown panel answered which release brought it back:

PostHog breakdown of the coupon issue by app version

Its bars put 44 of the occurrences on 1.0.0 and the newer 20 on 1.1.0, and the same panel splits by any custom property.

Releases themselves were harder. They come from source map uploads, and by default PostHog names them after the git commit:

PostHog releases list with a release named after a git SHA

The release is chosen at the inject step, so passing --release-version only to upload changed nothing. With the flags on inject as well, 1.2.0 got its own release. The issue's Releases panel also stayed empty until events carried $app_version with the dollar sign.

TrackJS: Fixed is a label

TrackJS lets you set a status from a short menu:

TrackJS Set Status menu

Fixed, Investigating and the rest are labels with no version attached. Here's the coupon row once 1.1.0 traffic had run:

TrackJS coupon error still marked Fixed after 1.1.0

It grew from 7 to 12 events and still said Fixed. The only sign it came back was inside the group:

TrackJS coupon group versions chart with 1.1.0 in red

With Show Versions on, 1.1.0 appears in red and "Versions Impacted" reads 2. The Versions page compares error counts only, and on 1.2.0 it made the bad release look like the healthiest:

TrackJS versions page with 1.2.0 showing the fewest errors

1.2.0 had 68 errors against 102 on 1.0.0, because slow checkouts let less traffic through. That's a limit of counting errors alone, and a reason not to judge a release on this page.

Release comparison

Capability PostHog TrackJS
Fixed bug returns ✔: reopened automatically ✘: stays Fixed
Regression label ✘: shown as Active ✘: none
Resolve in a future version ✘: not offered ✘: not offered
Errors by version ✔: Breakdown on any property ✔: Show Versions chart
Release records ✔: from source map uploads, git commit by default ✘: versions only
Notify affected users ✔: "Tell them it's fixed" ✘: not offered

Verdict: PostHog. It caught the returning bug, which TrackJS didn't. You'd still want a regressed label, and you'll need to pass the release flags at the right step.

PostHog reopened the coupon bug as an ordinary Active issue, and TrackJS kept it marked Fixed. I shipped the same unfixed release to Better Stack, which labelled the comeback:

Better Stack errors list with the coupon bug marked Reoccurred

Only that row turned to a red "Reoccurred", so the bug that came back stands apart from the ones that never left.

Catch a fix that didn't hold

Performance monitoring

On 1.2.0, the checkout's book lookup turned slow. An error tracker without performance data can't see that unless something fails.

PostHog: The slow query by version, in two clicks

PostHog's Operations view lists every span name with p50, p95 and error rate. Filtered to the previous release, checkout looked healthy:

PostHog operations filtered to release 1.1.0

There, checkout's p95 sat at 4.21 ms, and the version facet offers each of the three releases. Across all versions, the same table changed:

PostHog operations across all releases

POST /checkout reached a p95 of 4.76 s, and further down, SELECT book for checkout had a p95 of 2.93 s against a p50 of 0.15 ms. Sorting by p95 and filtering by version found it.

One false lead came first: GET /search at 1.23 s. Its database span was tiny, and the delay lived in untraced code ahead of it on every release.

TrackJS: A duration in one timeline

TrackJS doesn't measure request timing. The slowdown showed up only because one slow checkout also failed:

TrackJS timeline with a 4,073 ms checkout request

That checkout request ran for 4,073 ms, where 1.0.0 checkouts had finished in roughly 15 ms. The server error for the same request had the app's own slow query warning in its telemetry, because the agent keeps console.warn lines. Slow checkouts that succeeded left nothing at all.

Performance comparison

Capability PostHog TrackJS
Request latency ✔: p50, p95 and p99 per operation ✘: not measured
Slow query ✔: own span with percentiles Only as an app log line in a failed request
Latency by release ✔: version facet ✘: not offered
Successful slow requests ✔: recorded ✘: invisible

Verdict: PostHog. It found the slow query and the release that caused it. TrackJS isn't built for this, and it saw the slowdown only by accident.

PostHog found the slow query through its percentiles, which is a fair result, and TrackJS saw it only in one failed request. In Better Stack, I opened a single 1.2.0 checkout and got both problems in one request:

Better Stack checkout spans with the slow book lookup and failed charge

The lookup took 1.53 s of a 2.19 s request, and the rejected charge 660 ms. The logs for that request were a filter away:

Better Stack logs for one checkout with the slow-query warning

The slow-query warning TrackJS caught by luck shows here for any request you filter to.

Find the query behind a slow release

Logs and events

Logs explain what an error page can't, such as what the app was doing before the throw.

PostHog: A logs product linked to traces

PostHog took the app's logs over OpenTelemetry into its own viewer:

PostHog logs viewer with level and service facets

It held 1,068 logs in the hour, 968 Info and 100 Error, with Patterns, Group and Live tail views and a SQL tab. From a trace, the span panel's Logs tab lists the logs for that request:

PostHog trace span panel with its Logs tab

That's three logs for the whole checkout trace, one click from the error.

TrackJS: Console lines as telemetry

TrackJS has no logs product. Console output appears in each error's timeline as telemetry, which is how the slow-query warning surfaced. On the server, that buffer also held older lines from other requests, so it isn't a reliable per-request log.

Logs comparison

Capability PostHog TrackJS
Log storage and search ✔: own viewer with SQL ✘: not offered
Logs per request ✔: from the trace Partly: console lines on errors
Live tail ✔: included, not tested ✘: not offered

Verdict: PostHog. It stores and links logs. TrackJS keeps console lines only when an error happens.

Alerts

Neither tool sent me an alert, because I didn't connect a channel to either. Here's what each offers.

PostHog's issues list warns you about it directly:

PostHog issues list banner about missing alerts

It can alert on new, reopened and spiking issues once you pick a destination such as Slack, Teams or PagerDuty. Spike detection and severity rules sit in Configuration.

TrackJS configures notifications per application:

TrackJS notification settings for one application

Summary emails go out daily and weekly unless you turn them off. New Errors, Trending Errors and Error Rate Increases go to email, Slack or Teams. New Errors keys on message text, so a returning Fixed error never triggers it, and each new payment amount would.

Capability PostHog TrackJS
New errors ✔: new issues ✔: new messages
Returning bugs ✔: reopened issues ✘: not offered
Spikes or trends ✔: spiking issues ✔: trending and rate increases
Channels Slack, Teams, PagerDuty, others Email, Slack, Teams
Uptime and status pages ✘: not offered ✘: not offered

Verdict: Not compared. Neither tool delivered an alert in my test. On paper, only PostHog alerts on a bug that comes back.

AI-assisted debugging

Both tools offer an assistant on the error, and I tested both under the same condition: no repository connected, so neither could read the code beyond the stack.

PostHog: Fluent, and willing to invent

From any PostHog issue, "Open Fix prompt" hands a fix or explain prompt to PostHog AI or to an external coding agent. For the coupon error, PostHog AI got the mechanism right: the code checks that a coupon was sent and never checks that it exists. Then it filled the gap with guesses:

PostHog AI guessing the contents of the coupon table

The coupon codes it lists aren't the app's real ones. On the payment error, it went further:

PostHog AI recommended next steps for the payment error

It said the amount in the message caused the two issues, although each issue held several amounts. It took "upstream timeout" at face value, sent me to the gateway's status page, and wrote a "plausible reconstruction" of code it never saw. It didn't connect the error to 1.2.0 or the slow query.

TrackJS: Careful, and clear about its limits

TrackJS's AI Debugger runs when you open the tab and sends sanitized error data to OpenAI. On the payment error, it placed the failure and the release:

TrackJS AI Debugger likely cause for the payment error

It put the throw at server.js:173 on 1.2.0, ruled out a declined card, and used the timeline to note the retry attempt. Its fixes treated the two problems separately:

TrackJS AI Debugger fix list for the payment error

One item flags the slow book lookup as its own latency issue, a step PostHog AI never took. Like PostHog, it trusted the error message and didn't find the every-fifth-charge counter. On the wishlist error, it said outright that the report didn't include enough source code to choose between two causes.

AI comparison

Capability PostHog AI TrackJS AI Debugger
Where it runs PostHog AI, or sent to coding agents On every error, by default
Code access in my test ✘: stack frames only ✘: event data only
Payment error cause ✘: blamed the message and the gateway Partly: location and release right
Slow query ✘: not mentioned ✔: named as a separate issue
Invented details ✘: guessed coupons and code ✔: said when evidence was missing

Verdict: TrackJS. With the same lack of code, it stayed within its evidence and kept the slowdown apart from the payment error. PostHog AI's hand-off to coding agents may do better with a repository, which I didn't test.

Neither assistant here found the every-fifth-charge failure, and PostHog AI invented the code it couldn't see. I asked Better Stack's AI SRE what broke in 1.2.0, also with no repository:

Better Stack AI SRE comparing database calls across releases

It noticed that only checkout's queries had slowed to seconds, and then it counted the charges:

Better Stack AI SRE with the failed charge count for 1.2.0

Ten of 48 charges failed, every one on 1.2.0, so both problems came with a size. That answer took about four minutes and $0.49.

Ask AI SRE about your last deploy

Pricing

PostHog bills each product by usage above a monthly free tier, with no base fee. TrackJS bills by page view, and errors don't count toward the bill.

These prices come from each vendor's pricing page. I read PostHog's on October 28 and TrackJS's on November 4, 2026, and rechecked PostHog's free tier on November 4. Prices change, so check them again yourself.

Free plans and trials

Item PostHog Free TrackJS
Price Free, no card ✘: no free plan
Trial Not applicable Free trial, 1 million page views in mine
Errors 100,000 exceptions a month Not billed
Session replay 5,000 recordings a month ✘: not offered
Logs 10 GB a month ✘: not offered
AI 500 PostHog AI credits a month AI Debugger during my trial
Projects 1 Not stated for the trial

PostHog's plan step showed the same free tier on both its plans:

PostHog plan step with Free and Pay-as-you-go

On Free, events past the allowance are dropped. The pay-as-you-go plan has no base fee, permits six projects and keeps recordings for 90 days, where Free keeps them for one month.

My TrackJS trial ran on these terms:

TrackJS billing page during the trial

Entry-level paid plans

Item PostHog Pay-as-you-go TrackJS Starter TrackJS Professional
Base price $0 $49 monthly, $45 a month yearly $99 monthly, $91 a month yearly
Volume included The free tier above 100K page views 1M page views
Errors Billed above 100,000 20 saved a minute 60 saved a minute
Retention Recordings 90 days 10 days 15 days
Projects or applications 6 1 5

TrackJS Business costs $199 a month, or $183 yearly, for 5 million page views, 90 errors a minute and 30-day retention. TrackJS's page lists an AI Code Debugger but doesn't say which plans include it.

Usage beyond the free allowance

These are PostHog's per-unit rates from my October 28 reading. The November 4 page sent me to its calculator for them.

Item PostHog
Exceptions $0.000370 each from 100,001 to 325,000
Recordings $0.0050 each in the first paid band, 5,001 to 15,000
Logs $0.25 per GB
Spending caps ✔: per product

Comparing the actual cost

Picture a shop that gets 100,000 visits a month. On a yearly plan, TrackJS Starter costs $45 a month for that, with 10 days of history and one application. PostHog's free tier covers 100,000 exceptions, 5,000 recordings and 10 GB of logs before you pay anything, and it adds tracing, which TrackJS doesn't have.

The catch is the shape of the bill. TrackJS's price doesn't move when a release throws thousands of errors, because it only saves 20 a minute on Starter. PostHog's grows with exceptions and recordings past the free tier, so set its per-product caps before a bad deploy finds them for you.

Verdict: PostHog. For a small team it's free, and it covers more ground. TrackJS's flat page-view price is easier to predict, and cheap if browser errors are all you need.

Full feature comparison

Feature PostHog TrackJS
Browser errors ✔: after turning off the headless filter ✔: by default
Server errors ✔: with source lines ✔: file and line only
Automatic network errors ✘: not offered ✔: beside the app's reports
Source maps ✔: private upload ✔: public fetch or manual per error
Session replay ✔: off by default ✘: not offered
Activity timeline ✔: events and logs ✔: clicks, fetches, console
Browser to server Session ID and server trace ✔: correlation link
Grouping Stack frames, Node internals included Message text
Regression detection ✔: reopens, no label ✘: stays Fixed
Release records ✔: git commit by default ✘: versions only
Tracing ✔: OpenTelemetry ✘: not offered
Logs ✔: own product ✘: console telemetry only
Metrics Alpha, behind a preview ✘: not offered
Alerts Included, not tested Included, not tested
AI PostHog AI and coding-agent prompts AI Debugger on every error
Free plan ✔: 100,000 exceptions a month ✘: trial only

Which should you choose?

Decide how much of your stack one tool should watch.

Choose PostHog if you want errors next to everything else

PostHog gives an error its code, its recording, its trace and its logs, and it reopened the bug that came back. Its Operations view found the slow release on its own. The free tier is large enough to run a small product on.

The trade-offs are the setup and the grouping. Replay, headless capture and web exception autocapture each needed switching on, releases default to git commits, and on Node a timing change can split one bug into several issues. If you're weighing PostHog against a replay-first tool, our LogRocket vs PostHog comparison covers that side.

Choose TrackJS if your bugs live in the browser

TrackJS caught every failure by default, linked each failed fetch to its server error, and its AI Debugger was the more careful of the two. Its price is flat and low, and its timeline is the quickest read of what a page did before it broke.

It stops there. A Fixed error that returns stays Fixed, every value in a message makes a new group, and there's no performance data, so a slow release can look healthy. Its Express handler also needs the workaround described above. For a broader platform against it, see our Sentry vs TrackJS comparison.

FAQ

Does TrackJS have session replay like PostHog?

No. TrackJS keeps a telemetry timeline of clicks, inputs, navigations, fetches and console lines on each error. PostHog records the session and links it from the error.

Does PostHog group errors by message?

No. PostHog groups by stack frames, so different values in the message stay in one issue. On Node, its fingerprint also included internal async frames, which split one bug into two issues when a release changed the timing.

Can TrackJS monitor my Node.js server?

Yes, through trackjs-node, which reports server errors and links them to browser requests. In version 1.3.0, its Express error handler left 5xx requests hanging, so check your error responses after you install it.

Which is cheaper?

PostHog, if you stay within its free tier of 100,000 exceptions and 5,000 recordings a month. TrackJS has no free plan, and Starter costs $45 a month on a yearly plan for 100,000 page views.

Final thoughts

The real decision is between breadth and focus. PostHog shows you more of what happened around an error, and it asks you to configure, group and release-tag it carefully to get there. TrackJS gives you less to set up and less to see, and its AI was the more honest of the two about what it didn't know.

On the release that mattered, PostHog showed the slowdown and reopened the old bug, while TrackJS's version page suggested the deploy had gone well. If a quiet error count would reassure your team, TrackJS will reassure it at exactly the wrong moment.