block / block/buzz

load_skill silently truncates skill content over 32 KiB with no warning, log, or marker

Open
#4,163 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

**Describe the bug**

`load_skill` silently truncates any skill content over `MAX_SKILL_BODY_BYTES` (32 KiB) and returns the truncated text as if it were complete. There is no log line, no error, no in-band marker, and `is_error` stays `false` — so neither the model nor the caller can tell a truncated skill from a whole one.

The failure mode is a skill that loads successfully and behaves subtly wrong. Instructions in the tail of a SKILL.md — which is often where the output format, the refusal rules, or the verification checklist live — simply are not there, and nothing anywhere says so.

`crates/buzz-agent/src/builtin.rs` (both the SKILL.md path and the supporting-file path):

```rust
let output = if output.len() > MAX_SKILL_BODY_BYTES {
truncate_at_boundary(&output, MAX_SKILL_BODY_BYTES).to_owned()
} else {
output
};
```

returned as:

```rust
ToolResult {
provider_id: String::new(),
content: vec![ToolResultContent::Text(output)],
is_error: false,
}
```

Constant at `crates/buzz-agent/src/hints.rs:7`:

```rust
pub const MAX_SKILL_BODY_BYTES: usize = 32 * 1024;
```

The boundary handling itself is fine — `truncate_at_boundary` keeps the output valid UTF-8. The issue is purely that the truncation is unreported.

**Steps to reproduce**

1. Create a skill whose body exceeds 32 KiB, with a sentinel line at the very end:

```sh
mkdir -p .agents/skills/oversized
{
printf -- '---\nname: oversized\ndescription: Reproduces silent truncation of oversized skill bodies.\n---\n\n'
for i in $(seq 1 900); do
printf 'Filler line %s: padding to push this body past the 32 KiB cap.\n' "$i"
done
printf '\n## SENTINEL\n\nIf you can read this line, the skill was NOT truncated.\n'
} > .agents/skills/oversized/SKILL.md

wc -c .agents/skills/oversized/SKILL.md # ~57 KB, comfortably over 32768
```

2. Start an agent in that working directory so the skill is discovered (`.agents/skills` is the first entry in `SKILL_DIRS`).

3. Ask the agent to call `load_skill` with `oversized`, then ask it to quote the `## SENTINEL` section.

**Expected behavior**

One of:

- the truncation is reported — a `tracing::warn!` on the server side, and an explicit in-band marker appended to the returned text (e.g. `\n\n[truncated: N of M bytes shown]`) so the model knows its instructions are incomplete; or
- oversized skills are rejected at discovery with a clear error naming the file and its size, so the author fixes it rather than shipping a half-loaded skill.

Either is fine. Silence is the problem.

**Actual behavior**

The agent reports that the skill loaded, and the `## SENTINEL` section is absent. Nothing in the logs, the tool result, or the agent's own view indicates anything was dropped.

**Version and platform**

- Buzz version: relay crate `0.2.0`; verified against `main` @ `4632c55041c5d423d572a6f6411bb7b279c26f67`
- OS: macOS (arm64) — but this is platform-independent; the behaviour is in `buzz-agent`, read from source at the commit above

**Logs / additional context**

There are no logs — that is the bug.

Two notes that may help scoping:

1. The same truncation applies to supporting files read through `load_skill`, not just `SKILL.md`, so a skill can also lose the tail of a reference document it explicitly asked to read.
2. `MAX_HINTS_BYTES` (128 KiB, for the `AGENTS.md` chain) has the same shape and is presumably worth the same treatment, though the consequence there is milder since the chain is additive rather than a single authored document.

I'm reporting this from a source read rather than a captured session — I don't have a model credential wired to this checkout, so I could not paste a real transcript. The reproduction above is what I'd expect a maintainer with a working agent to see in under five minutes; happy to be corrected if the runtime path differs from what `builtin.rs` suggests.

A suggested minimal fix, if useful: keep the truncation, append a marker, and log once per truncated load —

```rust
let output = if output.len() > MAX_SKILL_BODY_BYTES {
tracing::warn!(
skill = %name,
bytes = output.len(),
limit = MAX_SKILL_BODY_BYTES,
"skill content truncated"
);
let mut t = truncate_at_boundary(&output, MAX_SKILL_BODY_BYTES).to_owned();
t.push_str(&format!(
"\n\n[truncated: {} of {} bytes shown]",
t.len(),
output.len()
));
t
} else {
output
};
```

Contributor guide

Open the contributing guide

Research direction

Start by reading both truncation branches in crates/buzz-agent/src/builtin.rs and the MAX_SKILL_BODY_BYTES definition in crates/buzz-agent/src/hints.rs. Run the oversized-skill reproduction from the issue, then verify that truncated SKILL.md and supporting-file loads clearly report incomplete content or reject the oversized file, without changing valid UTF-8 handling.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
ai-infra-agents
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.