Skip to content

fix(search): stop the rerank tokenizer panicking on trailing multi-byte uppercase runes - #569

Merged
zzet merged 1 commit into
mainfrom
fix/rerank-tokenize-unicode-panic
Aug 12, 2026
Merged

fix(search): stop the rerank tokenizer panicking on trailing multi-byte uppercase runes#569
zzet merged 1 commit into
mainfrom
fix/rerank-tokenize-unicode-panic

Conversation

@zzet

@zzet zzet commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Problem

A community user reported explore failing with:

tool "explore" internal error: runtime error: index out of range [1] with length 1

for the plain-text query простой текст on v0.63.2, and read it as a corrupted index. The index was fine — this is a per-request panic in the rerank tokenizer, caught by the tool firewall.

Root cause

tokenize's SCREAMING→Camel lookahead in internal/search/rerank/tokens.go guarded with i+1 < len(s) — a byte comparison (i comes from range s) — before reading []rune(s[i:])[1]. When a word ends in consecutive uppercase letters whose last rune is multi-byte (ТЕКСТ, ПРОСТОЙ, CAFÉ), the byte guard passes but the suffix decodes to a single rune, so the index panics with exactly the reported message.

The query itself was lowercase and harmless: rerank.Tokenize also runs over retrieved candidate text, so any repo containing all-caps non-ASCII words (Russian docs/strings in this case) trips it. Corpus-dependent, which is why it never reproduced on ASCII-only codebases.

Fix

Decode the lookahead rune with utf8.DecodeRuneInString(s[i+utf8.RuneLen(r):]) instead of materialising the suffix as a rune slice. This also drops the O(n) allocation per boundary check.

Tests

New tokens_test.go: the three panic inputs, the split-across-multibyte case (ЖКХStatusжкх, status), and the existing ASCII behaviors (ParseHTTPHeader, validate_user_token, HTTPHeader). go test -race ./internal/search/rerank/ and golangci-lint are green.

…te uppercase runes

tokenize's SCREAMING->Camel lookahead guarded with i+1 < len(s), a byte
comparison, before reading []rune(s[i:])[1]. When the current rune is a
multi-byte uppercase letter at the end of the string ("ТЕКСТ", "CAFÉ"),
the byte guard passes but the suffix decodes to a single rune, so the
index panics with "index out of range [1] with length 1". The panic
surfaced as an explore internal error for any query whose retrieved
candidates contained all-caps non-ASCII words.

Decode the lookahead rune with utf8.DecodeRuneInString instead, which
also drops the O(n) rune-slice allocation per boundary check.
@zzet
zzet merged commit 7ff32d7 into main Aug 12, 2026
10 checks passed
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