Tooling gotcha — RTK silently corrupts some shell commands
RTK (the token-optimizing CLI proxy that auto-rewrites shell commands via a Claude Code hook) does not just filter output — for some commands it mutates the command itself before running it, producing wrong results with no obvious sign that RTK was the cause. Two failure modes confirmed:
curl <url>gets the URL corrupted, and certain pipes/find-counts return empty or zeroed output.
This was hit while trying to fetch the actionlint binary during the FKITDEV-8239 review — curl failed, python3 urllib worked. It is the same family of hazard as rtk-git-log-hides-merge-commits (RTK output-filtering hides merge commits) and the long-standing RTK pipe-count mangling note in agent memory.
Symptoms (confirmed)
curl — malformed URL
curl: (3) URL using bad/illegal format or missing URL
# (libcurl: "Malformed input to a URL function")
RTK’s Rust proxy corrupts the URL string it passes through, so even a well-formed curl https://… fails with curl error (3). wget is also unreliable through the proxy.
Pipes / counts — empty or zeroed output
ls | sortreturned empty.find … | wc -land… | grep -c …get zeroed (count comes back0even when matches exist).- A
find … -o …expression came back empty during this same session.
The danger is that a zeroed count or empty listing looks like a legitimate “no results” answer — you can draw the wrong conclusion (e.g. “no file matches”) instead of realizing the command was mangled.
Git inspection commands — silently wrong answers (added 2026-08-11, FKITDEV-9197)
Four more modes, all confirmed during the CIB portal_css devel update. Every one of them returned a plausible answer rather than an error, which is what makes them dangerous:
| Invocation | What RTK did | Consequence |
|---|---|---|
git show <ref>:<file> | grep <pat> | Garbled the output so real matches were reported as absent | Nearly caused a wrong conflict-resolution decision — the reviewer concluded a symbol wasn’t there when it was |
diff -u <a> <b> | Reformatted into an unreadable line-offset view and reported a wrong exit code | Can’t tell same-vs-different, and the exit-code fallback lies too |
git diff --no-index <a> <b> | Rendered as if the whole file were new | Every line looks changed; the actual delta is invisible |
${PIPESTATUS[0]} | Blanked | Any pipeline-based pass/fail gate becomes unverifiable |
(A fifth — git log -1 showing the wrong commit right after a merge commit — is the merge-filtering behaviour documented in rtk-git-log-hides-merge-commits.)
Workarounds that worked:
rtk proxy <cmd> # raw, unfiltered — the reliable escape hatch
git grep <pattern> <ref> -- <path> # instead of `git show <ref>:<file> | grep`
shasum -a 256 <a> <b> # same-vs-different, immune to diff rendering
python3 -c "import difflib, sys; ..." # generate the diff inside pythonRule — re-verify anything you are about to assert
“The merge commit is X”, “the file does not contain Y”, “these two files are identical”, “the command exited 0” — none of these may rest on RTK-rendered output. Re-run under
rtk proxy, or prove it withgit grep/ a hash.rtk proxyis the preferred tool here precisely because the failure mode is silent: a python bypass is fine for fetching and counting, but for a verification claim you want the raw command.
Workarounds — bypass RTK with python3
python3 is not rewritten by the hook — route the real work through it
The proxy rewrites the shell command, but a
python3one-liner runs the actual logic inside the interpreter where RTK can’t touch the URL or the pipeline.
Download a file (instead of curl/wget):
python3 -c "import urllib.request,sys; urllib.request.urlretrieve(sys.argv[1], sys.argv[2])" <url> <dest>This is exactly how the actionlint binary was fetched after curl failed with error (3).
Run a multi-step CLI check (instead of a shell pipeline):
python3 - <<'EOF'
import subprocess
r = subprocess.run(["some-tool", "--flag", "arg"], capture_output=True, text=True)
print(r.returncode, r.stdout, r.stderr)
EOFUse subprocess.run([...]) with an argv list (no shell=True) so the command never round-trips through the rewriting hook. This sidesteps both the URL corruption and the pipe mangling. The vault scans in this very session (find files / grep contents) were done this way after find/grep returned empty.
Counts — cross-check two independent methods:
Don’t trust a single | wc -l / | grep -c. Compute the count two different ways (e.g. git ls-tree + a python len([...])) and confirm they agree before relying on the number. (See the agent-memory rtk-pipe-count-mangling note for the git-specific recipe.)
Escape hatch: rtk proxy <cmd> runs a command raw/unfiltered, but the user generally prefers agents avoid it — reach for the python bypass above first.
Related
- rtk-git-log-hides-merge-commits — sibling RTK gotcha: output-filtering drops merge commits from
git log --graph; use%p/rev-list --parentsplumbing instead. - RTK — the token-optimizing CLI proxy whose command rewriting causes this.
- FKITDEV-8239 — the depcheck-CI review during which the
curl-fails / python-urllib-works case was hit. - FKITDEV-9197 — the CIB / portal_css devel update where the four git-inspection modes above were found.
- raiffeisen-1.9.11.100 — where the
git show <rev>:<path>argument rewriting and the swallowed jest--verboselines were first recorded. - jest invocations — the consolidated table, in the procedure where it bites.