Skip to content

fix: clarify raw FTS query errors - #43

Open
miyaontherelay wants to merge 2 commits into
mainfrom
fix/fts-raw-mode-errors-and-docs
Open

fix: clarify raw FTS query errors#43
miyaontherelay wants to merge 2 commits into
mainfrom
fix/fts-raw-mode-errors-and-docs

Conversation

@miyaontherelay

Copy link
Copy Markdown
Contributor

Summary

FTS escaping was not broken: quote_fts_term already quotes and escapes non-raw terms, including hyphenated input. The actual issue was that --fts explicitly passes its input through as a raw FTS5 MATCH expression, where FTS5 interprets operators such as -.

This documents that raw-mode contract and maps malformed raw FTS5 expressions to an actionable error. Raw mode remains unescaped and unchanged.

Verification

  • . "$HOME/.cargo/env" && cargo build — passed
  • . "$HOME/.cargo/env" && cargo test --workspace — passed (58 unit tests)
  • Manual checks used an isolated temporary database seeded with GATE_FAIL parity:
$ ai-hist search --fts "parity-check"
Error: Invalid raw FTS5 MATCH expression. Quote literal terms (for example, "parity-check") or remove --fts to use the default search.

$ ai-hist search --fts GATE_FAIL
  #1     1970-01-01 01:00  (claude) [/tmp/fts-manual]  GATE_FAIL parity

$ ai-hist search parity
  #1     1970-01-01 01:00  (claude) [/tmp/fts-manual]  GATE_FAIL parity

$ ai-hist search --help
Search prompts and sessions

Usage: ai-hist search [OPTIONS] [QUERY]...

Arguments:
  [QUERY]...

Options:
      --source <SOURCE>
      --project <PROJECT>
      --tag <TAG>
      --role <ROLE>        [default: all]
      --agent
      --human
      --limit <LIMIT>      [default: 20]
      --fts                Pass the query through as a raw FTS5 MATCH expression. Operators such as `-`, `*`, `AND`, `OR`, and `NOT` are interpreted; quote literal terms yourself
      --json
  -h, --help               Print help

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@miyaontherelay, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37ac1860-e7df-44a6-aed0-8e47463bd3c9

📥 Commits

Reviewing files that changed from the base of the PR and between d6c4e22 and b921bbf.

📒 Files selected for processing (2)
  • crates/ai-hist-core/src/lib.rs
  • crates/ai-hist/src/lib.rs
📝 Walkthrough

Walkthrough

Raw FTS5 query errors are standardized in the core search library and applied to CLI history and event searches. CLI help now documents raw MATCH syntax, with tests covering quoted literals and friendly malformed-query errors.

Changes

Raw FTS5 error handling

Layer / File(s) Summary
Core FTS error conversion
crates/ai-hist-core/src/lib.rs
Adds shared raw-query error conversion, applies it during FTS iteration and collection, and tests quoted handling of parity-check.
CLI search integration
crates/ai-hist/src/lib.rs
Applies conversion to history and event searches, clarifies --fts help text, and tests friendly errors for malformed raw queries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: khaliqgant, kjgbot

Poem

A bunny found a query awry,
And taught its errors how to fly.
With quotes around each careful word,
Raw FTS commands are clearly heard.
Hop, hop—friendly search is nigh!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: improving raw FTS query error handling.
Description check ✅ Passed The description is directly related to the changeset and explains the raw FTS behavior and error handling changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fts-raw-mode-errors-and-docs

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.

@miyaontherelay
miyaontherelay marked this pull request as ready for review July 29, 2026 06:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ai-hist-core/src/lib.rs`:
- Around line 474-482: Restrict raw_fts_query_error to convert only confirmed
SQLite FTS5 MATCH syntax errors into the user-facing malformed-query message;
return the original error for all other SQLite and decoding failures. In
crates/ai-hist-core/src/lib.rs lines 547-552, preserve both mappings while
ensuring non-FTS query and collection errors propagate; apply the narrowed
conversion to history searches at crates/ai-hist/src/lib.rs lines 2594-2597 and
event searches at lines 2636-2639.

In `@crates/ai-hist/src/lib.rs`:
- Around line 4983-5005: Expand malformed_raw_fts_query_has_a_friendly_error to
cover every affected search path: retain the SearchRole::All assertion, add a
SearchRole::Assistant case to exercise the event-row branch after history lookup
succeeds, and add a direct core search invocation with raw_fts enabled. Apply
the same friendly-error and exclusion assertions to each case, reusing the
existing query setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a89db227-aaf4-4458-816d-8d07a0281933

📥 Commits

Reviewing files that changed from the base of the PR and between e83902b and d6c4e22.

📒 Files selected for processing (2)
  • crates/ai-hist-core/src/lib.rs
  • crates/ai-hist/src/lib.rs

Comment thread crates/ai-hist-core/src/lib.rs
Comment thread crates/ai-hist/src/lib.rs
The first pass converted every rusqlite error into the malformed-expression
message whenever raw mode was on, so I/O, decoding and genuine schema errors
during a raw search were relabelled as FTS5 syntax problems. That traded one
bad error surface for a worse one.

is_fts5_syntax_error() now identifies actual FTS5 parse failures (messages
naming fts5, malformed match, unterminated string, or the 'no such column'
bareword misparse); everything else propagates untouched.

Test expanded to cover SearchRole::All, SearchRole::Assistant (the event-row
branch reached after history lookup succeeds) and a direct ai_hist_core::search
call with raw_fts enabled, via a shared assert_friendly_fts_error helper.
@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Both findings were valid and are fixed in b921bbf.

1. Error mapping was too broad — real bug, thanks

The first pass discarded the original error whenever raw was true, so an I/O failure, a decoding error, or a genuine schema mismatch during a raw search would all have surfaced as "Invalid raw FTS5 MATCH expression." That traded one bad error surface for a worse one: the original complaint was a confusing error, and the fix would have hidden unrelated failures entirely.

Now narrowed:

fn is_fts5_syntax_error(error: &rusqlite::Error) -> bool {
    let rusqlite::Error::SqliteFailure(_, Some(message)) = error else {
        return false;
    };
    let m = message.to_ascii_lowercase();
    m.contains("fts5")
        || m.contains("malformed match")
        || m.contains("unterminated string")
        || m.starts_with("no such column")
}

no such column is included because that is how FTS5 reports a bareword parsed as a column reference — the exact shape of the original parity-check failure. Everything else propagates untouched.

I considered interpolating the underlying error into the friendly message for diagnosability, but the existing assertions require raw SQLite text to stay out of user-facing output, and your finding was about masking non-FTS errors rather than surfacing FTS ones. Kept the message clean and let the predicate do the work.

2. Test coverage expanded

malformed_raw_fts_query_has_a_friendly_error now exercises all three paths through a shared assert_friendly_fts_error helper:

  • SearchRole::All
  • SearchRole::Assistant — the event-row branch reached after history lookup succeeds
  • ai_hist_core::search invoked directly with raw_fts enabled

Verification

cargo build clean; cargo test --workspace green (31 + 27, 0 failed).

I also mutation-checked the test rather than trusting a green run — forcing is_fts5_syntax_error to return false:

predicate forced false → test FAILED (panicked at lib.rs:5030)
predicate restored     → test ok

So the assertion is genuinely load-bearing on the narrowing, not just on the message text.

Live CLI, unchanged semantics confirmed:

$ ai-hist search --fts "parity-check"
Error: Invalid raw FTS5 MATCH expression. Quote literal terms (for example,
"parity-check") or remove --fts to use the default search.

$ ai-hist search parity
#9040754 2026-07-28 23:32  (codex) [...]   ← non-raw path unaffected

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