fix(search): map bare duration to transaction.duration in search parser#114473
fix(search): map bare duration to transaction.duration in search parser#114473
duration to transaction.duration in search parser#114473Conversation
wmak
left a comment
There was a problem hiding this comment.
I don't think this is the right fix, can you link me the 500? I can take a look. But duration alone like this should be treated as the tag duration rather than the transaction.duration
BYK
left a comment
There was a problem hiding this comment.
Thanks for the review @wmak! You're right — duration should be treated as a tag, not remapped.
Here's the 500: https://sentry.sentry.io/issues/7452286129/
The root cause trace:
duration:>3shits the PEG grammar as aduration_filternodeis_duration_key("duration")→False(onlytransaction.durationis induration_keys)- Falls through to text filter:
SearchFilter("duration", "=", ">3s") - In
default_filter_converter,self.resolve_column("duration")atbase.py:1319resolves to the raw ClickHouse UInt32 column (viaDATASET_FIELDS[Transactions]match atsnuba.py:1640) instead oftags[duration] - This produces
WHERE duration = '>3s'— type mismatch →SchemaValidationError→ 500
So the real bug is in step 4: resolve_column("duration") should NOT resolve to the internal column when called from the search filter path. The DATASET_FIELDS check at snuba.py:1639-1642 is too eager — it exposes internal column names to user queries.
I'll revert the key_mappings approach and look at the correct fix. Should this be addressed in resolve_column (preventing internal-only names from being exposed), or somewhere in the builder's filter resolution path?
Bare `duration` is an internal Snuba column name (UInt32) for `transaction.duration`. When users write `duration:>3s`, the search parser correctly treats it as a text filter (since `duration` is not in `duration_keys`), but `resolve_column` resolves it to the raw ClickHouse column via `DATASET_FIELDS`, producing a string-vs-numeric mismatch that crashes Snuba with a 500. Add `duration` to `SKIP_FILTER_RESOLUTION` so it resolves to `tags[duration]` in filter context — harmless (no results) instead of a 500. Reverts the `key_mappings` approach from the previous commit per reviewer feedback: bare `duration` should be a tag lookup, not remapped to `transaction.duration`.
494f1f6 to
14d48fd
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 14d48fd. Configure here.
…parser (#114473) ## Summary - Adds `"duration"` to `SKIP_FILTER_RESOLUTION` in `constants.py` so bare `duration` in search filters resolves to `tags[duration]` instead of the internal ClickHouse column - Fixes a 500 Internal Server Error when users write `duration:>3s` instead of `transaction.duration:>3s` ## Root Cause Bare `duration` is an internal Snuba column name (UInt32, milliseconds) for `transaction.duration`. It is NOT in `duration_keys` or `numeric_keys`, so the search parser correctly treats it as a text filter — but `resolve_column` then resolves it to the raw ClickHouse column via `DATASET_FIELDS`, creating a string-vs-numeric mismatch that crashes Snuba with a 500. When a user writes `duration:>3s`: 1. The PEG grammar matches it as a `duration_filter` node 2. `is_duration_key("duration")` returns `False` → falls through to text filter handling 3. The value becomes the string `">3s"` (operator + value concatenated) 4. `resolve_column("duration")` finds it in `DATASET_FIELDS[Transactions]` as the raw UInt32 column 5. This produces `WHERE duration = '>3s'` — string vs UInt32 mismatch → Snuba 500 ## Fix Add `"duration"` to `SKIP_FILTER_RESOLUTION` (alongside the existing `total_count` and `total_transaction_duration` entries). This forces `default_filter_converter` to resolve it as `tags[duration]` instead of the internal column — harmless (no results from a nonexistent tag) instead of a 500.

Summary
"duration"toSKIP_FILTER_RESOLUTIONinconstants.pyso baredurationin search filters resolves totags[duration]instead of the internal ClickHouse columnduration:>3sinstead oftransaction.duration:>3sRoot Cause
Bare
durationis an internal Snuba column name (UInt32, milliseconds) fortransaction.duration. It is NOT induration_keysornumeric_keys, so the search parser correctly treats it as a text filter — butresolve_columnthen resolves it to the raw ClickHouse column viaDATASET_FIELDS, creating a string-vs-numeric mismatch that crashes Snuba with a 500.When a user writes
duration:>3s:duration_filternodeis_duration_key("duration")returnsFalse→ falls through to text filter handling">3s"(operator + value concatenated)resolve_column("duration")finds it inDATASET_FIELDS[Transactions]as the raw UInt32 columnWHERE duration = '>3s'— string vs UInt32 mismatch → Snuba 500Fix
Add
"duration"toSKIP_FILTER_RESOLUTION(alongside the existingtotal_countandtotal_transaction_durationentries). This forcesdefault_filter_converterto resolve it astags[duration]instead of the internal column — harmless (no results from a nonexistent tag) instead of a 500.