Skip to content

feat(errors): fingerprint v3 grouping with a version-salted hash - #1466

Merged
Makisuo merged 8 commits into
mainfrom
feat/error-fingerprint-v3
Oct 14, 2026
Merged

Makisuo merged 8 commits into
mainfrom
feat/error-fingerprint-v3

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

What

Ports the v3 fingerprint into production grouping, with the v3.1 rules from a replay of every prod fingerprint of the last 30 days.

Frames:

  • A namespaced exception type (contains /) keeps only its throw site, when it has a non-empty message signature. With no message it keeps 3 frames, since frames are all that is left. Generic types keep the top 3.
  • Effect span frames ((definition)) and a schema error's at [..] key-path lines are not frames.
  • Frames lose package versions in store paths (@1.2.3), (index N), file:///bundle/, repeated bundle hashes and Astro asset hashes.

Issue message signature (ERROR_MSG_REDACTIONS; MSG_TEXT_REDACTIONS is unchanged because the AI tool-error fingerprint and the web's masked display share it):

  • ANSI colour codes are stripped.
  • A ClickHouse error loses only the query it echoes (after in scope / while processing), keeping the identifier, function or type that names the bug.
  • Filesystem-rooted paths become PATH. The root must end at /, . or a delimiter, so /datasets/x stays. Tilde paths match only as ~/....
  • Prefixed ids (org_..., cus_..., with a digit or capital in the id part, so top_operations stays) and ULIDs become ID.
  • An HTTP status after HTTP/status/returned/code/failed keeps 1xx-4xx and 500 exact and folds 501-599 into 5xx.
  • JSON value redaction also covers UUID dashes.

Also: the warehouse client-timeout message names its query, so timeouts from different queries stay separate issues.

FINGERPRINT_VERSION goes to 3 and is hashed as FINGERPRINT_HASH_SALT ('fp3', the 6th cityHash64 argument).

The salt matters. Retention archives every error issue below the current version, and the upsert never clears archivedAt. Without the salt, live issues whose hash v3 leaves unchanged would be archived and never come back. With it, every hash rotates and archiving on sight is correct. It also makes any future bump safe.

Ships as the error_events_mv / error_events_by_time_mv change, ClickHouse migration 0039_error_fingerprint_v3 (frozen DDL), and local store schema v30.

Evidence

  • Replay of all 3,572 prod fingerprints from the last 30 days: 3,572 → 2,022 issues (-43%). Visible issues 1,553 → 1,207 (-22%). Issues re-opened by a deploy 595 → 187 (-69%). 0 wrong merges across 332 audited merges. 2 splits, both intended: 404 vs 500, and DNS 1016 vs 5xx. TS mirror in 100% parity with production's hashes.
  • ClickHouse e2e (error-fingerprint-v3.clickhouse.e2e.test.ts): for every rule, the view's hash equals cityHash64 of the TS mirror's inputs, 22 seeds.
  • Unit cases for each rule, plus guards: different route paths, quoted table names and missing identifiers stay distinct, generic TypeErrors at different callers stay distinct, /datasets/x and ~5 min stay, 401 vs 404 vs 500 stay distinct, 502/520/524 merge.
  • Typecheck (domain, backend, cli, query-engine), fingerprint and migration tests, errors service, query-engine, CLI migration and both ClickHouse e2e suites pass locally.

Rollout impact

  • Every open error issue in every org gets a new hash. Retention archives the old issues, and the live bugs reopen as new triage issues. Assignee, priority, notes, snooze, resolution and linked PRs do not carry over, and notifications may fire for the new issues as if they were first seen. Worth a heads-up, or a quiet deploy window.
  • Issue history (sparkline, samples) restarts at cutover. Old error_events rows keep their hashes until the 90-day TTL ages them out.

Archived issues now recover when they recur

Retention archives on the premise that an issue stopped occurring, and the tick never cleared archivedAt. So an issue archived too early stayed invisible while it kept accumulating occurrences. The version bump made that a real risk in two places, and both are now handled:

  • Self-hosted ClickHouse orgs keep producing v2 hashes until they run "Apply schema" for migration 0039 (the errors tick reads their warehouse). Their open issues are no longer lost: each comes back on its next occurrence with its state intact and its version restamped. Once they apply 0039, the old issues stop receiving occurrences and age out through the normal window.
  • Deploy ordering between the worker constant and the view no longer strands issues. Whichever ships first, an issue archived in the gap comes back if it occurs again.

Also fixed: a regression on an archived, long-resolved issue used to reopen it while leaving it archived, so the regression stayed hidden. It now reopens unarchived. An occurrence from a pre-fix build still leaves a resolved issue archived.

Summary by CodeRabbit

  • Improvements
    • Error grouping now better accounts for variable message details and stack frames, helping similar errors share a fingerprint while distinguishing different errors.
    • Query timeout messages now identify the affected query.
  • Bug Fixes
    • Archived issues can reappear when new occurrences are detected, while resolved issues remain archived when an occurrence matches a build already recorded as resolved.

…rrors:replay

The Resolved tab filtered a mixed page of the 50 most recently seen issues in
the browser, so it rendered empty, with no "Load more", whenever that page was
all open issues. Done issues archived after 14 days never showed at all.

- v2 GET /error_issues takes `state=open|closed`, `search` and
  `include_archived`; the web list asks the server for exactly its view.
- Tabs are Open / Closed / All (old `view` values still decode). Closed holds
  verifying, done, cancelled and wontfix, archived ones included.
- A debounced search box (`?q=`) over type, message, label and service.
- The stat strip keeps error events and their share of spans; the toolbar
  counts issues.
- `bun run errors:tick` fires the alerting minute cron on a local stack.
- `bun run errors:replay` groups a sample of error events with today's
  fingerprint and a proposed v3, checks parity against production hashes,
  and lists the largest merges.
… errors:tick runs

The iOS OpenAPI check failed on the new list params. A pasted log line longer
than the API's 200-char search cap now clamps instead of failing the page. The
minute cron also runs the alert and escalation lanes; the script and doc say so.
A namespaced exception type keeps only its throw site, Effect span frames
are dropped, frames lose package versions and (index N), and the issue
message signature cuts ClickHouse query echoes and redacts filesystem
paths, prefixed ids and ULIDs. JSON values also redact UUIDs.

FINGERPRINT_VERSION goes to 3 and is hashed as FINGERPRINT_HASH_SALT, so
the bump rotates every hash. Without the salt, retention would archive live
issues whose hash v3 leaves unchanged, and nothing un-archives them.

Ships as Tinybird MV change, ClickHouse migration 0039 and local schema v30.
Replay on a fresh prod sample: TS mirror in parity with production hashes,
19% fewer issues on the top 100 fingerprints, no splits.
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: db247ef6-5c58-4a81-94ea-53e8fa87b71c
📥 Commits

Reviewing files that changed from the base of the PR and between 84c8e96 and f68d27e.

⛔ Files ignored due to path filters: 3
  • bun.lock is excluded by !**/*.lock
  • packages/domain/src/generated/clickhouse-schema.ts is excluded by !**/generated/**
  • packages/domain/src/generated/tinybird-project-manifest.ts is excluded by !**/generated/**
📒 Files selected for processing: 25
  • apps/chat-bot/package.json
  • apps/cli/src/server/local-schema-history.ts
  • apps/cli/src/server/local-schema-version.ts
  • apps/cli/src/server/local-store-migrations/steps.ts
  • apps/cli/src/server/schema-identity.ts
  • apps/cli/src/server/schema/local-inserts.json
  • apps/cli/src/server/schema/local-schema-v30.sql
  • apps/cli/src/server/schema/local-schema.sql
  • apps/cli/test/local-store-migrations.test.ts
  • apps/cli/test/native-local-store-migration.sh
  • apps/ingest/src/clickhouse_insert_mappings.rs
  • apps/web/src/components/infra/cloudflare/cloudflare-worker-table.tsx
  • packages/backend/src/services/errors/ErrorsService.test.ts
  • packages/backend/src/services/errors/error-tick-fingerprint.clickhouse.e2e.test.ts
  • packages/backend/src/services/errors/error-tick-persistence.ts
  • packages/backend/src/services/warehouse/error-events-attribute-fallback.clickhouse.e2e.test.ts
  • packages/backend/src/services/warehouse/error-fingerprint-v3.clickhouse.e2e.test.ts
  • packages/domain/src/clickhouse/ddl-emitter.test.ts
  • packages/domain/src/clickhouse/migrations/0039_error_fingerprint_v3.ts
  • packages/domain/src/clickhouse/migrations/index.ts
  • packages/domain/src/tinybird/fingerprint.test.ts
  • packages/domain/src/tinybird/fingerprint.ts
  • packages/domain/src/tinybird/materializations.ts
  • packages/query-engine/src/execution/executor.ts
  • scripts/errors-replay/candidate.ts
💤 Files with no reviewable changes: 2
  • apps/chat-bot/package.json
  • apps/web/src/components/infra/cloudflare/cloudflare-worker-table.tsx

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

The pull request adds version 3 error fingerprinting across TypeScript, ClickHouse, and the local schema. It updates issue persistence for archived issues that receive occurrences. It also removes a chat-bot dependency and a Cloudflare loading component, and adds the query pipe name to a timeout error.

Changes: Error fingerprint v3

Layer / File(s) Summary
Fingerprint inputs and normalization
packages/domain/src/tinybird/fingerprint.ts, packages/domain/src/tinybird/materializations.ts, packages/domain/src/tinybird/fingerprint.test.ts, packages/domain/src/clickhouse/ddl-emitter.test.ts, scripts/errors-replay/candidate.ts
Fingerprint generation adds v3 message and frame normalization, filtering, and a version-derived hash salt. Tests cover the updated behavior, and the replay candidate now uses production fingerprint inputs.
ClickHouse fingerprint views
packages/domain/src/clickhouse/migrations/*, packages/backend/src/services/warehouse/error-events-attribute-fallback.clickhouse.e2e.test.ts, packages/backend/src/services/warehouse/error-fingerprint-v3.clickhouse.e2e.test.ts
Migration 39 recreates both error-event views with v3 fingerprints. ClickHouse tests check fingerprint parity and compare selected results from migrations 20 and 30.
Local schema version 30
apps/cli/src/server/local-schema-version.ts, apps/cli/src/server/local-schema-history.ts, apps/cli/src/server/local-store-migrations/steps.ts, apps/cli/src/server/schema-identity.ts, apps/cli/src/server/schema/local-schema.sql, apps/cli/src/server/schema/local-inserts.json, apps/cli/test/local-store-migrations.test.ts, apps/cli/test/native-local-store-migration.sh, apps/ingest/src/clickhouse_insert_mappings.rs
The local schema advances to version 30. The migration replaces error-event views; existing event rows retain v2 hashes and age out with retention. Schema identity, revision values, and migration checks are updated.
Archived issue updates
packages/backend/src/services/errors/error-tick-persistence.ts, packages/backend/src/services/errors/ErrorsService.test.ts, packages/backend/src/services/errors/error-tick-fingerprint.clickhouse.e2e.test.ts
Regressed archived issues are unarchived. Other observed archived issues are restored unless their workflow state is done. Tests cover fingerprint-version updates and preservation of issue workflow fields.

Chat-bot dependency

Layer / File(s) Summary
Remove chat-bot dependency
apps/chat-bot/package.json
The package manifest removes @maple-dev/effect-sdk.

Cloudflare worker loading component

Layer / File(s) Summary
Remove worker table loading component
apps/web/src/components/infra/cloudflare/cloudflare-worker-table.tsx
The module removes the exported loading component and its Skeleton import.

Query timeout message

Layer / File(s) Summary
Include pipe name in timeout error
packages/query-engine/src/execution/executor.ts
The client-timeout error message now includes the query’s pipe name.

Priority: ⬆️ High

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Spans
  participant ErrorEventViews
  participant ErrorsService
  participant ErrorIssues
  Spans->>ErrorEventViews: Materialize v3 fingerprint hashes
  ErrorEventViews->>ErrorsService: Provide fingerprint occurrences
  ErrorsService->>ErrorIssues: Update matching issue and archive status
Loading

Suggested reviewers: jeremyfunk

Merge Risk: ⚪ Minimal · up to f68d2

No actionable issue remains from this review; the change is mergeable after normal checks.

Pre-merge checks: Passed 5
Passed Passed checks: 5 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main changes: fingerprint v3 grouping and a version-salted hash.
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 20 files. (2 skipped: 2…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution failed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Frames: strip file:///bundle/, repeated bundle hashes and Astro asset
hashes, and ignore a schema error's `at [..]` key path. A namespaced type
keeps one frame only when its message signature is non-empty.

Messages: strip ANSI codes; cut only the query a ClickHouse error echoes
(after " in scope " / " while processing "), keeping the identifier; end a
filesystem root at a separator so /datasets survives, match ~/ paths only,
and catch adjacent paths; require a digit or capital in a prefixed id so
snake_case names survive; keep 1xx-4xx and 500 statuses exact and fold the
other 5xx into one class.

The MV computes _msgSig before the frame slice that now reads it. The
warehouse client timeout names its query, so timeouts from different
queries stay separate issues. FINGERPRINT_VERSION stays 3 with its salt;
migration 0039 and local schema v30 are regenerated in place.
Base automatically changed from worktree-errors-findability to main October 14, 2026 20:38
…t-v3

# Conflicts:
#	scripts/errors-replay/candidate.ts
…ng export

Both knip findings were already failing on main.
Retention archives on the premise that an issue stopped occurring, and the
tick's upsert never cleared archivedAt, so an issue archived too early stayed
invisible while it kept accumulating occurrences. A fingerprint bump makes
that likely: a self-hosted warehouse keeps producing old hashes until it
applies the new view, and a deploy gap does the same.

An archived open issue now comes back on its next occurrence with its state
intact and its version restamped. A regression on an archived resolved issue
reopens it unarchived. An occurrence from a pre-fix build leaves a resolved
issue archived, as before.
…ckHouse

Spans go through the real migrations and views, and the real tick writes the
issues to Postgres. Covers the grouping v3 intends and an archived issue that
a warehouse still on the v2 view brings back.
@Makisuo
Makisuo marked this pull request as ready for review October 14, 2026 21:14
@maple-review-bot

maple-review-bot Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 8/10 · likely safe to merge
The risky part — hash rotation, archiving and restore — is pinned end to end by two ClickHouse e2e suites; the 2,148-line v30 snapshot SQL went unread.
quality 100/100 · no findings · tests covered · risk high

Fingerprint v3: version-salted hashes, new frame and message-signature rules, a ClickHouse MV recreation (0039), local schema v30, and restore of archived error issues on recurrence. The MV SQL and its TS mirror apply the same chain, and the parity is pinned by ClickHouse e2e tests.

  • FINGERPRINT_VERSION is 3 and 'fp3' is hashed into FingerprintHash, so every issue hash rotates
  • Namespaced exception types keep one frame; Effect span and schema key-path lines are not frames
  • ERROR_MSG_REDACTIONS cuts ClickHouse query echoes and redacts FS paths, prefixed ids and ULIDs
  • The errors tick restores an archived, non-done issue on its next occurrence

Before merge

  • Migration · Check it is safe on production data and with the code still deployed · packages/domain/src/clickhouse/migrations/0039_error_fingerprint_v3.ts
  • Manual · Warn users before cutover: every open error issue is archived and returns as a new triage issue without assignee, priority, notes or snooze
  • Manual · Self-hosted ClickHouse orgs must apply schema migration 0039, or they keep producing v2 hashes that reopen archived issues
What was checked
  • MV SQL and TS mirror apply the same redaction order (materializations.ts:828, fingerprint.ts:202, 0039_error_fingerprint_v3.ts:23)
  • Both error_events MVs carry the salt and v3 frame rules (local-schema.sql:1091, :1161, :1259)
  • Frame selector matches in RE2 and JS: position(line,'(definition)') = 0 vs !line.includes(...)

f68d27e · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo merged commit fae1f9f into main Oct 14, 2026
45 checks passed
@Makisuo
Makisuo deleted the feat/error-fingerprint-v3 branch October 14, 2026 21:24
@maple-review-bot

Copy link
Copy Markdown

Merged with 3 steps from "Before merge" still open. If they are done, tick them on the review comment; if not, now is the time.

  • Manual · Self-hosted ClickHouse orgs must apply schema migration 0039, or they keep producing v2 hashes that reopen archived issues
  • Manual · Warn users before cutover: every open error issue is archived and returns as a new triage issue without assignee, priority, notes or snooze
  • Migration · Check it is safe on production data and with the code still deployed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant