feat(error-tracking): add the error-tracking program on the orchestrator flow - #1185
Conversation
…tor flow New `wizard error-tracking` command. Modeled on replay-vision: a detect step carries the framework id into orchestrator preflight, the context-mill `error-tracking` flow seeds install/init (integration-v2 step-skills) when the repo has no PostHog, then exception capture, then the source-map subgraph where the platform needs it. The detect step also pre-installs posthog-cli for symbol-upload platforms, which the agent cannot (warlock blocks npm install -g). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVXmLWB31CBujD2FJ2ogvY
🧙 Wizard CIRun the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands: Test all apps:
Test all apps in a directory:
Test an individual app:
Show more apps
Test against a Context Mill branch:
Add Results will be posted here when complete. |
Binding harness anthropic→pi, like metrics: the binding routes only, and every stage's model and effort are pinned context-mill side in the flow's frontmatter (terra seed, sol tasks, luna report). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVXmLWB31CBujD2FJ2ogvY
e2e.json flow definition + profile registration: the driver confirms the intro, answers the flow's api-key ask from SOURCE_MAPS_CLI_KEY (secret rule, vaulted by the ask tool) and declines the test-affordance offer. Two variations: the pinned orchestrator-on-pi binding and the linear fallback. Unlocks snapshots mode and the workbench e2e runner for the command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVXmLWB31CBujD2FJ2ogvY
The screen was registered in the action registry (satisfying the exhaustiveness test) but decideE2eAction keeps its own intro switch, and an unlisted screen falls through to wait — the driver sat on the intro until the host died. Adds the missing case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVXmLWB31CBujD2FJ2ogvY
The bin resolves --local-* flags in its yargs middleware into the local-dev singleton that getSkillsBaseUrl() reads. The tui-host bypasses yargs, so its env-backed local flags reached the session but never the singleton — agent prompts always resolved to production, and an unpublished flow died in preflight with "No seed agent prompt". Initialize the singleton from the same env spellings before the store starts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVXmLWB31CBujD2FJ2ogvY
The MCP facade runs ensureGitignoreCoverage after every write; the pi facade, which is the arm the orchestrator flows run on, did not. Projects whose .gitignore already lists .env hid it — an Xcode project's lists xcuserdata/ and nothing else, so the personal API key the error-tracking flow writes was left staged for the next `git add`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X5Kw3YahYz9RiFeJ8r2hxY
|
Let's fucking gooooooooo |
Resolve conflicts in scripts/tui-host.no-jest.ts (keep initLocalDev and the gateway setup) and switchboard.test.ts (error-tracking follows main's metrics case). Update the binding comment and the e2e summary for terra install/init. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SEsZQQrYjXPVEhQuoSwE6
…install - The interactive detect step now aborts with DetectNoFramework when detection finds nothing, like ciPreRun. Before, bootstrap put the program id on session.skillId and preflight failed with a misleading "failed to download" message. - error-tracking and error-tracking-upload-source-maps now call one once-per-process helper, preinstallPostHogCliOnce. A failed install is an event and a warning, no longer a captureException per machine. - The test derives the error-tracking CLI set from VARIANTS_REQUIRING_POSTHOG_CLI instead of a hardcoded list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SEsZQQrYjXPVEhQuoSwE6
…skill After detect, session.skillId holds the framework, so the lookup found the wrong skill or none. Use integration-v2-error-tracking-step, the skill every run of the flow installs, and drop the argument useSkillEntry does not take. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SEsZQQrYjXPVEhQuoSwE6
set_env_values refused POSTHOG_KEY on every call. In a project whose code already reads that name, the agent had to rename working code, and every deploy step and CI secret that still passed POSTHOG_KEY then started the app with an empty token. Both facades now share legacyKeyNameRefusal. It still refuses the name in a project that does not read it, so new projects keep the canonical names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SEsZQQrYjXPVEhQuoSwE6
🦔 PostHog Review reviewed this pull requestFound 2 must fix, 6 should fix, 2 consider. Published 10 findings (view the review). |
The flow no longer turns on Error Tracking on the backend. It relies on SDK autocapture only. Remove error_tracking:read and product_enablement:write from the program's scopes, and add a test that keeps product_enablement:write out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
The detect step ran `npm install -g @posthog/cli` before the intro screen, so Cancel left a global install behind. `run` is now a function, and `runAgent` resolves it after the intro gate and after `ciPreRun`. KMP skill variants have no `framework` tag, so preflight aborted every KMP run. The detect step and `ciPreRun` now stop KMP with an unsupported-platform exit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z
The menu expands a bundle into per-framework entries, so no entry keeps the bundle id. Match the group when the other rules find nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z
Add shell scripts, Gradle and Xcode config, Makefile, Procfile and the package/app/eas JSON files to the scan that keeps a POSTHOG_KEY name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z
…k and tips The program detected one framework in the install dir before the intro, so a monorepo root got no choice. A post-login screen now scans the repo and lets the user pick the project, as the legacy upload-source-maps program did. The run step's targetDir scopes the orchestrator run to that project, and run-wizard (and the e2e host) walk the steps when a step sets targetDir. The run screen plays the source-maps learn deck, worded for platforms that upload nothing, then error-tracking tips instead of the generic ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z
Agent DAG for the
|
gewenyu99
left a comment
There was a problem hiding this comment.
Ohhh man this is much cleaner than I thought. I also have comments for you to confirm, but tentative stamp/ Running rn
Review feedback: error-tracking's detect adapter copied self-driving's match of each scanned project to an Integration. toIntegrationCandidates in project-scope now does it once. Each program keeps its own classification, and the error-tracking adapter documents how its rule differs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z

Part of a 3-PR set
Problem
Error tracking setup takes several commands today.
wizard upload-source-mapsalso stops when the repo has no PostHog.Changes
New command
wizard error-tracking. It runs theerror-trackingorchestrator flow on pi.src/lib/programs/error-tracking/: a project picker after login that scopes the run to the picked project,ciPreRunfor headless runs, a learn deck and error-tracking tips.run-wizardand the e2e host walk the steps when a run step setstargetDir. No other program changes path.posthog-clipre-install for swift, android, react-native, flutter, go and rust. Warlock blocksnpm install -gfor the agent.bin.ts, e2e profile.Shared changes, for all programs:
set_env_valuesadds the env file to.gitignore, like the MCP facade.set_env_valuesacceptsPOSTHOG_KEYwhen the project already reads it.error-trackingandupload-source-mapsshare oneposthog-clipre-install helper. A failed install sends an event, not an exception.tui-hostinitializes the local-dev settings.Test plan
pnpm build && pnpm test && pnpm fixpass (2,680 tests).Tests
Per fixture: run the wizard, add an error trigger, build, run, and read the issue
back from PostHog. A trace that resolves to the original source shows that the
source-map upload works. Project: Test (Oli).
✅ node-webpack
node-webpack— webpack 5 + ts-loader,posthog-nodealready in the manifest but its key defined nowhere. The run wired@posthog/webpack-pluginwithdevtool: 'hidden-source-map'anddeleteAfterUpload, and wrote the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_ID; releasewizard-e2e-node-webpack. Triggered bynode dist/index.js.TypeError: Cannot read properties of undefined (reading 'retries') — minified
lt/ctresolved toreadRetryBudget/kaboomin./src/index.ts.Build output
Stack trace
✅ node-rollup
node-rollup— Rollup 4, no TS plugin,posthog-nodealready in the manifest but its key defined nowhere. The run wired@posthog/rollup-pluginwithdeleteAfterUploadbeside the existingsourcemap: true, and wrote the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_ID; releasewizard-e2e-node-rollup. Triggered bynode dist/index.js.TypeError: Cannot read properties of undefined (reading 'retries') — resolved
readRetryBudget->kaboomin../src/index.ts, with source context lines.Build output
Stack trace
✅ node-rollup-typescript-plugin
node-rollup-typescript-plugin— Rollup 4 +@rollup/plugin-typescript,posthog-nodealready in the manifest but its key defined nowhere. The run wired@posthog/rollup-pluginafter the TypeScript plugin withdeleteAfterUpload, added astartscript for the bundle, and wrote the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_ID; releasewizard-e2e-node-rollup-typescript-plugin. Triggered bynpm run start.TypeError: Cannot read properties of undefined (reading 'tsRetryBudget') — compiled
dist/index.jsframes resolved back to the.tssource, with context lines.Build output
Stack trace
✅ node-raw
node-raw— plaintscbuild,posthog-nodealready in the manifest but its key defined nowhere, whilestartalready rannode --env-file=.env. The run installed@posthog/cliand appended aposthog-cli --dotenv-file .env sourcemap processstep tobuild, wrote the uploader'sPOSTHOG_CLI_API_KEY/POSTHOG_CLI_PROJECT_ID/POSTHOG_CLI_HOSTto match that flag; releasenode-raw. Triggered bynpm run start.TypeError: Cannot read properties of undefined (reading 'rawRetryBudget') — compiled
dist/index.jsframes resolved back to the.tssource, with context lines.Build output
Stack trace
✅ next
next— Next.js 16 on Turbopack,posthog-jsalready in the manifest but its key defined nowhere. The run wired@posthog/nextjs-configviawithPostHogConfigwithdeleteAfterUpload, and wroteNEXT_PUBLIC_POSTHOG_KEY/_HOSTbeside the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_IDin.env.local; releasewizard-e2e-next. Clicked in Chromium againstnext start.TypeError: Cannot read properties of undefined (reading 'nextRetryBudget') — minified
o/iresolved toreadNextRetryBudget/kaboomNextinapp/KaboomButton.tsx.Build output
Stack trace
✅ nuxt-3-6
nuxt-3-6— Nuxt 3.6,posthog-jsalready in the manifest andruntimeConfigfalling back to a literalphc_placeholder, so nothing threw while events went nowhere. The run replaced that fallback with a realNUXT_PUBLIC_POSTHOG_KEY, addedsourcemap.clientand anitro:build:public-assetshook runningposthog-cli sourcemap inject/upload --delete-after, with the uploader'sPOSTHOG_CLI_*in.env; releasewizard-e2e-nuxt-3-6. Clicked in Chromium againstnuxt preview.TypeError: Cannot read properties of undefined (reading 'nuxt36RetryBudget') — minified
s/rresolved toreadNuxt36RetryBudget/kaboomNuxt36incomponents/KaboomButton.vue.Build output
Stack trace
✅ react-vite
react-vite— Vite 7 + React,posthog-jsalready in the manifest but its key defined nowhere. The run addedbuild.sourcemap: 'hidden', wired@posthog/rollup-pluginthroughloadEnvwithdeleteAfterUpload, and wroteVITE_PUBLIC_POSTHOG_KEY/_HOSTbeside the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_ID; releasewizard-e2e-react-vite. Clicked in Chromium againstvite preview.TypeError: Cannot read properties of undefined (reading 'viteRetryBudget') — one minified
assets/index-DM2dbKgY.jsline resolved to the exact lines ofsrc/KaboomButton.tsx. Rollup's minified map carries nonamesentry for the two helpers, so those frames keep their mangled_c/gclabels while file and line are exact.Build output
Stack trace
✅ nuxt-4-3
nuxt-4-3— Nuxt 4.3,posthog-jsalready in the manifest, a client plugin readingruntimeConfig.public.posthogKey, and noruntimeConfigblock defining it. The run took the@posthog/nuxtmodule path:sourcemap.client: 'hidden',sourcemapExcludeSources: false, aposthogConfig.sourcemapsblock, andNUXT_PUBLIC_POSTHOG_KEY/_HOSTbeside the uploader'sPOSTHOG_API_KEY/POSTHOG_PROJECT_ID; releasewizard-e2e-nuxt-4-3. Clicked in Chromium againstnuxt preview.TypeError: Cannot read properties of undefined (reading 'nuxt43RetryBudget') — minified
s/rresolved toreadNuxt43RetryBudget/kaboomNuxt43inapp/components/KaboomButton.vue.The module and the pre-existing client plugin both call
posthog.init, so the page logsYou have already initialized PostHog! Re-initializing is a no-op. Capture works; the plugin's owncapture_exceptionsoptions are the part that gets dropped.Build output
Stack trace
✅ python-flask
python-flask— Flask 3, no PostHog at all. The seed classified it as a readable-stack platform and queued no uploader subgraph: four tasks, nocredentials/configure/wire-ci. The run installedposthogandpython-dotenv, wiredload_dotenv()with aPosthog(...)client underenable_exception_autocapture, added an@app.errorhandler(Exception)that re-raisesHTTPExceptionand callscapture_exceptionfor the rest, and wrotePOSTHOG_PROJECT_TOKEN/POSTHOG_HOST. Triggered byGET /kaboom(port 8000).TypeError: 'NoneType' object is not subscriptable — frames arrived readable with no symbol set, which is the point of the skip.
Task graph (no uploader subgraph)
Stack trace
✅ cicd-github-actions-nested-docker-node-raw
cicd-github-actions-nested-docker-node-raw— the first fixture wherewire-ciruns. GitHub Actions → a local composite action → a multi-stagedocker build→ an SSH deploy, so a name has to survive four hops. Verified by deriving thedocker buildanddocker runcommands from the generated YAML itself, with${{ github.* }}resolved from a simulated runner context, then reading the trace back.Build-time credentials: workflow
secrets→ action inputs →build-argsfor the non-secret settings, andsecrets:+RUN --mount=type=secretfor the personal API key, so it never lands in a layer. Release identity: the fiveGITHUB_*vars forwarded asARGandENV— docker build args are not inherited from the environment, so without this the CLI has neither them nor.gitand fails the build on a runner exactly as it does locally. Runtime: thedocker run -enames match the names that the source reads.TypeError: Cannot read properties of undefined (reading 'ghaRetryBudget') — captured from the container started with the deploy step's own variable names, resolved
readGhaRetryBudget->kaboomGhain../src/index.tsagainst maps uploaded during the image build.docker buildexits 0 with noSecretsUsedInArgOrEnvwarning.docker build output (command derived from action.yml, github.* simulated)
Stack trace
✅ cicd-docker-node-raw
cicd-docker-node-raw— multi-stage Docker with no CI pipeline: its own header says the image is built and run by hand. The interesting case for release identity, since there is no provider to inherit git context from and.gitis in.dockerignore. Verified by running the hand-writtendocker buildanddocker runthe Dockerfile documents.No
GITHUB_*forwarding here — correctly, since nothing would fill it. Instead the build command carries both halves outright,--release-nameplus--release-version 0.1.0taken from the manifest, so it resolves with no git and no CI. Credentials:ARG/ENVfor the non-secret settings andRUN --mount=type=secretfor the personal API key.TypeError: Cannot read properties of undefined (reading 'handRetryBudget') — captured from the container, resolved
readHandRetryBudget->kaboomHandin../src/index.tsagainst maps uploaded during the image build.docker build output (hand-run, no CI variables)
Stack trace
✅ cicd-github-actions-docker-node-raw
cicd-github-actions-docker-node-raw— GitHub Actions with the Docker build inlined in the workflow, no composite action: the same provider branch as the nested fixture, one hop shorter. Verified by deriving thedocker buildfrom the workflow's ownbuild-push-actionstep with${{ github.* }}simulated, then running the image with the deploy step's variable names.GITHUB_*forwarded asARG+ENV(a provider exists here, unlike the hand-built fixture),secrets:+RUN --mount=type=secretfor the personal API key,build-argsfor the non-secret settings. Runtime names were already consistent — the source readsPOSTHOG_KEYand the deploy step passes-e POSTHOG_KEYfromsecrets.POSTHOG_KEY— and the run left both halves alone.TypeError: Cannot read properties of undefined (reading 'inlineRetryBudget') — resolved
readInlineRetryBudget->kaboomInlinein../src/index.tsagainst maps uploaded during the image build.The project token is supplied by CI at deploy time and is not written to the local
.env; the run reports it as a follow-up rather than inventing a value.docker build output (command derived from deploy.yml, github.* simulated)
Stack trace
✅ cicd-github-actions-single-stage-docker-node-raw
cicd-github-actions-single-stage-docker-node-raw— GitHub Actions, single-stage Dockerfile. The one where secret handling actually matters: there is no throwaway build stage, so whatever the build writes into the image ships to production. Verified by deriving thedocker buildfrom the workflow, then inspecting the resulting image for the key.The personal API key crossed the boundary on
RUN --mount=type=secretand is absent from the shipped image — not inConfig.Env, not indocker history, and not on the filesystem.GITHUB_*forwarded asARG+ENVfor the release identity. Note thatPOSTHOG_CLI_PROJECT_ID,POSTHOG_CLI_HOSTand theGITHUB_*values do persist as imageENV— inherent to a single-stage build, and none of them are secrets.TypeError: Cannot read properties of undefined (reading 'singleRetryBudget') — resolved
readSingleRetryBudget->kaboomSinglein../src/index.tsagainst maps uploaded during the image build.docker build + secret-leak checks on the shipped image
Stack trace
✅ rust
rust— Cargo release build,posthog-rsalready in the manifest, native debug symbols rather than source maps.[profile.release]gaineddebug = "line-tables-only",split-debuginfo = "packed"andstrip = "none"; aMakefilerunscargo build --releasethenposthog-cli symbol-sets upload --directory target/release --include-source, and itsruntarget exports.envbefore the binary. Capture iscapture_panics(true)onErrorTrackingOptionsBuilder, passed through.error_tracking(...)and installed withposthog_rs::init_global, plusdotenvyso the token is reachable.kaboom: rust retry budget unavailable — an uncaught panic, resolved to
rust_app::kaboom_rust2->rust_app::read_rust_retry_budget2with the tokio runtime frames named too.symbol upload output (cargo release build)
Stack trace
✅ cicd-github-actions-node-raw
cicd-github-actions-node-raw— GitHub Actions, no Docker: the build runs directly on the runner, output is copied to the server, and the app restarts over SSH. This is the over-application check for the release-identity rule, and it passes: noGITHUB_*forwarding at all, correct because the runner already sets them and the skill says nothing needs passing.POSTHOG_CLI_*are scoped to the Build step'senv:rather than the whole job, the personal key coming fromsecrets.. The deploy step passes the variable names thatsrc/index.tsreads.capture-exceptionswas skipped deliberately —initsetenableExceptionAutocapture: trueon the singleton and the report says so.generated workflow (reviewed, not executed)
✅ cicd-gitlab-node-raw
cicd-gitlab-node-raw— GitLab CI: build job compiles and uploads, deploy job rsyncs to the server and restarts over SSH. Credentials stay out of the YAML entirely — the job inheritsPOSTHOG_CLI_*from Settings → CI/CD → Variables, and the report names exactly which masked variables to add. NoGITHUB_*-style forwarding: the job checks the repo out, so.gitgives the release its commit directly.The deploy step passes the variable names that
src/index.tsreads.The build job ran inside its declared image,
node:24-alpine, and succeeded.build job replayed in its declared image (node:24-alpine)
✅ cicd-ssh-vps-node-raw
cicd-ssh-vps-node-raw— the build happens on the VPS, over SSH: the workflow connects, pulls, and runsnpm run buildthere. A third boundary shape, and the release-identity rule reads it correctly — GitHub's variables do not exist on the VPS, soGITHUB_ACTIONS/SHA/REF_NAME/REPOSITORY/SERVER_URLare forwarded onto the remote build command, alongsidePOSTHOG_CLI_*, scoped to that one command rather than exported for the whole session.The restart line passes the variable names that
src/index.tsreads.generated deploy step (reviewed, not executed)
✅ go
go— plain Go module,posthog-goalready in the manifest, native debug symbols rather than source maps. The run wrote aMakefilethat picks the linker flags per platform (-compressdwarf=falseon darwin,-B gobuildidon linux, a hard error elsewhere) and follows the build withposthog-cli --dotenv-file .env symbol-sets upload --directory ./bin --include-source. Itsruntarget carries-include .envplusexport POSTHOG_PROJECT_TOKEN POSTHOG_HOST, so the binary actually receives the token.Capture is a deferred
recover()boundary at the process entry: it enqueues the panic with an explicit distinct id, closes the client to flush, then re-panics so Go keeps its normal crash behaviour.runtime error: invalid memory address or nil pointer dereference — triggered by
make run, resolved tomain.kaboomGo2->main.readGoRetryBudget2inmain.goagainst symbols uploaded during the build.symbol upload output (make build)
Stack trace
✅ ios-spm
ios-spm— SwiftUI app on an Xcode project with posthog-ios already added through Swift Package Manager, so the run skipped install and init and went straight to capture plus dSYM upload.config.errorTrackingConfig.autoCapture = truewent in immediately before the singlePostHogSDK.shared.setup(config). On the target's Release configurationDEBUG_INFORMATION_FORMATbecamedwarf-with-dsym, and a final Run Script phase calls the SDK's bundled uploader verbatim —POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/posthog-ios/build-tools/upload-symbols.sh"— with$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)as its Input File. Credentials landed in a gitignored.envbeside the.xcodeproj,POSTHOG_CLI_HOSTset to the API host. No CI task ran — the repository carries no pipeline.Built Release for the iOS 26.5 simulator, installed and launched it, and the exception came back with every frame resolved: retry budget is unreadable (Code: 42).
dSYM upload output (Release build phase)
Stack trace
✅ ios-pod
ios-pod— the same SwiftUI app on CocoaPods instead of SPM, PostHog 3.59.1 from the lockfile, so again the run skipped install and init and went to capture plus dSYM upload.config.errorTrackingConfig.autoCapture = truewent in beforePostHogSDK.shared.setup(config);DEBUG_INFORMATION_FORMATbecamedwarf-with-dsymon both Release configurations, and a Run Script phase ordered after[CP] Embed Pods Frameworkscalls the pod-bundled uploader —POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh"— with$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)as its Input File. It picked the CocoaPods path over the SPM one on its own. Credentials went to a.envthe run added to.gitignore,POSTHOG_CLI_HOSTset to the API host. No CI task ran — the repository carries no pipeline.Built Release against the workspace for the iOS 26.5 simulator, installed and launched it, and the exception came back with every frame resolved: shipping quote is unavailable (Code: 77).
dSYM upload output (Release build phase)
Stack trace
✅ android
android— Gradle project withposthog-androidalready in the manifest, so the run skipped install and init. Android uploads R8 mapping files rather than source maps.errorTrackingConfig.autoCapture = truewent onto thePostHogAndroidConfigin the Application class. In the app module'sbuild.gradle.kts(never the root project) the run appliedid("com.posthog.android") version "1.5.0"— the plugin is versioned separately from the SDK, and it did not reuse the SDK's3.55.2— flipped the release build type'sisMinifyEnabledtotruewith itsproguardFilesline untouched, since the plugin only hooks minified variants, and bridged the credentials in with atasks.withType<PostHogCliExecTask>().configureEach { … }block reading a gitignored root.env. No CI task ran — the repository carries no pipeline../gradlew :app:assembleReleaseranuploadPostHogProguardMappingsRelease, then the APK went onto an API 36.1 emulator. R8 had renamed both helper functions toy; PostHog resolved them back from the uploaded mapping: android inventory count is unavailable.mapping upload output (./gradlew :app:assembleRelease)
Stack trace
✅ no-posthog-express
no-posthog-express— Express server with no PostHog at all, so the run installed and initialised the SDK first. It addedposthog-nodeplusdotenv, built the client withenableExceptionAutocapture: truebehind a guard that throws outside production when the token is missing, and mountedsetupExpressRequestContextbefore the routes andsetupExpressErrorHandlerafter them. Thebuildscript gainedposthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name no-posthog-expressafter esbuild, and.envcarries the runtime pair (POSTHOG_PROJECT_TOKEN,POSTHOG_HOST) alongside the threePOSTHOG_CLI_*upload variables.The test error is an uncaught route error rather than a manual capture, so it travels the error-handler path: it arrives
handled: falsewithmechanism.type: middleware, resolved to original source — Cannot read properties of undefined (reading 'expressRetryBudget').Build output
Stack trace
✅ no-posthog-fastify
no-posthog-fastify— Fastify server with no PostHog, so install and init ran first. The run addedposthog-nodeanddotenv, built the client withenableExceptionAutocapture: truebehind the missing-variable guard, and wired capture through Fastify's own hook —app.setErrorHandler((error, request, reply) => { posthog?.captureException(error); reply.send(error); })— rather than a try/catch per route. Thebuildscript chainsposthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name no-posthog-fastifyafter esbuild's ESM bundle, and.env.exampledocuments all five variables: the runtime pair and the threePOSTHOG_CLI_*.An uncaught route error travels the
setErrorHandlerpath and resolves to original source — Cannot read properties of undefined (reading 'fastifyRetryBudget').Build output
Stack trace
✅ no-posthog-hono
no-posthog-hono— Hono on@hono/node-serverwith no PostHog, so install and init ran first. The run addedposthog-nodeanddotenv, built the client withenableExceptionAutocapture: truebehind the missing-variable guard, and captured through Hono's ownapp.onErrorhook — passing aHTTPException's own response through when the error carriesgetResponse, and falling back to a 500 otherwise, so capture does not change the app's error semantics. Thebuildscript chainsposthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name no-posthog-honoafter esbuild's ESM bundle.An uncaught route error travels the
onErrorpath and resolves to original source — Cannot read properties of undefined (reading 'honoRetryBudget').Build output
Stack trace
✅ no-posthog-koa
no-posthog-koa— Koa with no PostHog, so install and init ran first. The run addedposthog-nodeanddotenv, built the client withenableExceptionAutocapture: truebehind the missing-variable guard, and captured through Koa's ownapp.on('error', …)event rather than wrapping middleware. Thebuildscript chainsposthog-cli --dotenv-file .env sourcemap process --directory dist --release-name no-posthog-koaafter esbuild, and.env.exampledocuments all five variables.An uncaught route error reaches the
app.on('error')listener and resolves to original source — Cannot read properties of undefined (reading 'koaRetryBudget').Build output
Stack trace
✅ no-posthog-node-http
no-posthog-node-http— a barenode:httpserver with no PostHog and no framework, so there is no error hook to mount. The run addedposthog-nodeanddotenvand leaned onenableExceptionAutocapture: truealone, which installs the SDK's process-level handler; it added no handler of its own, which is the right call here. Thebuildscript chainsposthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name no-posthog-node-http --delete-after, so the.mapis uploaded and then removed rather than shipped beside the bundle.The test error is a real uncaught crash — the process dies and the connection drops — so the SDK has to capture and flush during teardown. It arrives
handled: falsewithmechanism.type: onuncaughtexception, resolved to original source — Cannot read properties of undefined (reading 'nodeHttpRetryBudget').Build output
Stack trace
✅ no-posthog-javascript-web
no-posthog-javascript-web— vanilla browser JS on Vite with no PostHog, so install and init ran first.posthog.initsetscapture_exceptionsas an explicit object (capture_unhandled_errorsandcapture_unhandled_rejectionson,capture_console_errorsoff), which in posthog-js takes precedence over the project's remote setting rather than deferring to it. Upload is the@posthog/rollup-plugininvite.config.jswithsourcemaps.enabledanddeleteAfterUpload: true, readingPOSTHOG_API_KEY/POSTHOG_PROJECT_ID/POSTHOG_HOSTvialoadEnv; the run added aversiontopackage.jsonsoreleaseVersionresolves. The builtdist/assets/ships the.jswith no.mapbeside it.The test error is thrown from a click handler and left uncaught, so it reaches PostHog through
window.onerror—handled: false— with the minifiedYh/Zhresolved back to real names: Cannot read properties of undefined (reading 'jsWebRetryBudget').Build output
Stack trace
✅ no-posthog-next
no-posthog-next— Next 16 app router with no PostHog, so install and init ran first. Init went intoinstrumentation-client.ts(capture_exceptions: true), and the run addedapp/global-error.tsxso React render errors are captured too — two paths, one init. Upload iswithPostHogConfiginnext.config.tswithsourcemaps.deleteAfterUpload, guarded by a ternary so a missing key falls back to the plain config instead of failing the type check. Credentials are split the way Next wants them: the public pair in.env.local, the build-timePOSTHOG_API_KEY/POSTHOG_PROJECT_IDin.env.The upload runs inside
runAfterProductionCompile— 25 chunks uploaded, 7 already present. An uncaught click-handler error reaches PostHog throughwindow.onerror(handled: false) and resolves to original source: Cannot read properties of undefined (reading 'nextRetryBudget').Build output
Stack trace
✅ no-posthog-vue
no-posthog-vue— Vue 3 on Vite with no PostHog, so install and init ran first. Init is insrc/main.js, and capture goes through Vue's ownapp.config.errorHandlerrather than a global window hook, so component and handler errors are caught where Vue surfaces them. Upload is the@posthog/rollup-plugininvite.config.jswithsourcemaps.deleteAfterUpload, spread in conditionally so a project without upload credentials still builds.The click handler throws and Vue routes it to
errorHandler— the page never sees awindow.onerror, which is how you can tell the wired path is the one that fired. Minifiedi/s/tall resolve back: Cannot read properties of undefined (reading 'vueRetryBudget').Build output
Stack trace
✅ no-posthog-react-router
no-posthog-react-router— React Router 7 in framework mode (SSR) with no PostHog, so install and init ran first. Init went intoapp/entry.client.tsxahead ofhydrateRoot, withcapture_exceptionsas an explicit object. Upload is the@posthog/rollup-plugininvite.config.tsalongsidereactRouter(), withbuild.sourcemap: 'hidden'anddeleteAfterUpload— and because framework mode runs two Vite passes, the plugin uploads twice: 5 client chunks, then 1 server chunk.An uncaught click-handler error reaches PostHog through
window.onerror(handled: false), with minifieds/iresolved back through the hidden maps: Cannot read properties of undefined (reading 'rrRetryBudget').Build output
Stack trace
✅ no-posthog-sveltekit
no-posthog-sveltekit— SvelteKit 2 with no PostHog, so install and init ran first. The run wired both sides the way SvelteKit expects:src/hooks.client.tsdoes the browser init ininit()(capture_exceptions: true) and captures viahandleError, whilesrc/hooks.server.tscaptures server errors through aposthog-nodeclient insrc/lib/server/posthog.tsandawaits a flush before returning. BothhandleErrorhooks return{ message, status }unchanged, so capture does not alter what the user sees. Upload is the@posthog/rollup-pluginbesidesveltekit(), spread in conditionally; it runs on both Vite passes — 14 client chunks then 15 server.An uncaught click-handler error resolves to original source through the maps: Cannot read properties of undefined (reading 'svelteRetryBudget').
Build output
Stack trace
✅ no-posthog-angular
no-posthog-angular— Angular 21 standalone on the stock@angular/buildbuilder, no PostHog. This is the platform with no environment to read from, and the run solved it the documented way: aprebuild/prestartscript generatessrc/environments/environment.generated.tsfrom.env, so the committed source holds no token. Capture is Angular's ownErrorHandler, provided at bootstrap — it unwrapsngOriginalError, pulls the real error out of anHttpErrorResponse, and only then captures. Because that handler already sees every sync error, init deliberately setscapture_unhandled_errors: falsewhile leaving rejections on, so nothing is captured twice. Upload chainsposthog-cli sourcemap process --directory ./dist/browserafterng build --configuration production.The click never reaches
window.onerror— Angular intercepts it — and the trace resolves through the Angular runtime into app source: Cannot read properties of undefined (reading 'angularRetryBudget').Build output
Stack trace
✅ no-posthog-django
no-posthog-django— Django with no PostHog. Python stack traces are already readable, so the seed correctly skipped the whole upload subgraph: nocredentialsask, no build config touched, no CLI dependency — the run wentintro → auth → run → outrowith no interruption at all. It installedposthogandpython-dotenv, addedload_dotenvand thePOSTHOG_PROJECT_TOKEN/POSTHOG_HOSTsettings, registeredposthog.integrations.django.PosthogContextMiddleware, and built the client in anAppConfig.ready()withenable_exception_autocapture=True, publishing it assettings.POSTHOG_MW_CLIENT(the name the middleware reads) and registeringatexitshutdown so buffered events flush on exit.An uncaught view error resolves straight to source, no symbolication involved: 'NoneType' object is not subscriptable.
run output (no build step — upload subgraph skipped)
Stack trace
✅ no-posthog-nuxt
no-posthog-nuxt— Nuxt 4.3 with no PostHog, so install and init ran first. Init isapp/plugins/posthog.client.ts, readingruntimeConfig.public.posthogand capturing through Nuxt's ownvue:errorhook. Upload is the CLI recipe from the Nuxt reference, copied verbatim into thenitro:build:public-assetshook —posthog-cli sourcemap injectthensourcemap upload --delete-afterover.output— withsourcemap.clienton andnitro.rollupConfig.output.sourcemapExcludeSources: falseso the uploaded maps carry their sources.The hook choice is the load-bearing detail:
nitro:build:public-assetsfires before Nitro writes the asset manifest that records each file's size, so the injected chunk ids are accounted for. Served bytes match disk exactly (Content-Length: 432520), the page loads clean, and the trace resolves: Cannot read properties of undefined (reading 'nuxtRetryBudget').Build output
Stack trace
LLM context
Built with Claude Code. The flow follows
replay-vision(wizard) anderror-tracking-upload-source-maps(context-mill).🤖 Generated with Claude Code
https://claude.ai/code/session_01668rN31F4aotxmenNksz4Z