rtk-ai / rtk-ai/rtk

rtk gain panics with 'attempt to add with overflow' when any command has negative saved_tokens

Open Beginner friendly
#3,958 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:cli bug priority:high resolved-pending-close
Dominant language
Rust
Stars
81.1k
Forks
5.1k
Avg merge
4d 21h
Merged PRs (30d)
35

Description

Summary

A single tracked command whose RTK output is larger than its input makes rtk gain panic — permanently, for every later invocation, until the row is deleted by hand.

saved_tokens is stored as a signed integer and can legitimately be negative (RTK produced more output than the raw command). gain.rs reads it back as usize, so -51 becomes 18446744073709551565, and the running total overflows on the next add.

Reproduction

Any row with saved_tokens < 0 triggers it. On my machine four such rows arrived from a normal test run:

$ sqlite3 ~/.local/share/rtk/history.db \
    "select original_cmd, input_tokens, output_tokens, saved_tokens
       from commands where saved_tokens < 0;"
git worktree list|0|51|-51
find . -name '*.txt'|3|25|-22
find . -name 'cargo.toml'|3|25|-22
find . -name secret.txt|0|22|-22

$ rtk gain
thread 'main' (221433) panicked at src/core/tracking.rs:851:13:
attempt to add with overflow

rtk gain --history fails the same way. Deleting the negative rows restores both.

Cause

src/core/tracking.rs:841 casts the column straight to usize:

row.get::<_, i64>(3)? as u64,      // exec_time_ms
row.get::<_, i64>(1)? as usize,    // input
row.get::<_, i64>(2)? as usize,    // output
row.get::<_, i64>(3)? as usize,    // saved  <- negative wraps here

then src/core/tracking.rs:851:

total_saved += saved;              // overflow in debug, silently absurd in release

In a release build there is no panic — the totals and the savings percentage are simply nonsense, which is arguably worse.

Notes

  • git worktree list shows input_tokens = 0, output_tokens = 51, i.e. the raw command produced nothing and RTK's version produced 51 tokens. Whatever the merits of that, the analytics must survive it.
  • The "never worse" guard does not prevent these rows from being recorded.
  • Distinct from the UTF-8 char-boundary panics (#3232, #3733, #2318, #2954, #2787) — this one is an integer overflow and does not depend on the command text.
  • Distinct from #3853 ("No tracking data yet"), which is an empty database rather than a crash.

Suggested fix

Read the column as i64 and accumulate in i64, so negative savings subtract as they should, then clamp only at the presentation layer (a negative total is real information — it means RTK cost tokens over that window). A saturating cast would hide the signal.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at src/core/tracking.rs lines 841 and 851, where saved_tokens is read and added to the running total. Reproduce the issue with the negative saved_tokens rows shown in the report, then check both rtk gain and rtk gain --history. Done means negative savings no longer cause overflow or panic and the reported totals remain meaningful.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, sqlite
Domain
cli, databases
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.