voidzero-dev / voidzero-dev/vite-task
vt_shell: command spans are character indices but sliced as bytes (plan.rs:185)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 466
- Forks
- 42
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 19
Description
vt_shell: command spans are character indices but sliced as bytes (plan.rs:185)
vp run <script> aborts with a slice panic before the script runs when the
script's command string contains a non-ASCII character. The Range<usize>
returned by try_parse_as_and_list counts characters, but every consumer
slices the command string by bytes, so the range can end in the middle of a
multi-byte character.
Reproduced with vp v0.3.0 (vite-task rev d05b1dc), macOS 26.6.2 arm64.
Filing here rather than on the vite-plus tracker the panic handler points at,
since the code lives in this repo — happy to move it if you prefer.
Panic
thread '<unnamed>' (28859855) panicked at crates/vt_plan/src/plan.rs:185:65:
end byte index 78 is not a char boundary; it is inside '置' (bytes 77..80 of string)
Reproduction
A package.json is the whole repro — no dependencies, no install:
{
"name": "pm-probe",
"version": "1.0.0",
"scripts": {
"whoami": "node -e \"console.log('agent =', process.env.npm_config_user_agent || '(未设置)')\""
}
}
$ vp run whoami
thread '<unnamed>' panicked at crates/vt_plan/src/plan.rs:185:65:
end byte index 78 is not a char boundary; it is inside '置' (bytes 77..80 of string)
The command string is 78 characters but 84 bytes, and its last multi-byte
character 置 occupies bytes 77..80 — so the character-based end index 78 lands
inside it. Position matters, not merely the presence of non-ASCII: the same
script with the Chinese text earlier in the line happens to survive, because the
byte index it produces is still a valid boundary (it just slices the wrong text).
Root cause
crates/vt_shell/src/lib.rs:95:
fn pipeline_to_command(pipeline: &Pipeline) -> Option<(TaskParsedCommand, Range<usize>)> {
let location = pipeline.location()?;
let range = location.start.index..location.end.index;
brush_parser::tokenizer::SourcePosition::index is documented as
The 0-based index of the character in the input stream.
while every consumer of that range slices a &str, i.e. by byte:
crates/vt_plan/src/plan.rs:185— the panic sitecrates/vt_plan/src/plan.rs:266crates/vt_plan/src/plan.rs:281
(The same range is also stored through Context::push_stack_frame, though
nothing slices with it today.)
All three sites sit under the // Build execution display comment, so what
aborts the run is display bookkeeping, not the work itself.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with crates/vt_shell/src/lib.rs:95 and inspect how the range from SourcePosition is consumed in crates/vt_plan/src/plan.rs at lines 185, 266, and 281. Run the supplied package.json reproduction, then add or run a regression covering a non-ASCII command and verify that vp run completes without a slice panic.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100