agentscope-ai/agentscope · error · SkillUploadError
{entry.path!r} declared {entry.size} bytes but sent {written
Error message
{entry.path!r} declared {entry.size} bytes but sent {written}. What it means
Raised by tar_stream when the total bytes read for a manifest entry do not exactly equal the declared entry.size (fewer bytes were sent). Because tar archives require exact per-entry lengths followed by block padding, a short read means either the source stream ended early or the declared size is too large. The upload is aborted with SkillUploadError before a corrupt archive is persisted.
Source
Thrown at src/agentscope/app/_service/_workspace.py:478
"""
for entry, upload in zip(manifest.entries, files):
info = tarfile.TarInfo(name=entry.path)
info.size = entry.size
info.mtime = 0
yield info.tobuf(tarfile.GNU_FORMAT)
written = 0
while chunk := await upload.read(_CHUNK_SIZE):
written += len(chunk)
if written > entry.size:
raise SkillUploadError(
f"{entry.path!r} is larger than its declared "
f"{entry.size} bytes.",
)
yield chunk
if written != entry.size:
raise SkillUploadError(
f"{entry.path!r} declared {entry.size} bytes but "
f"sent {written}.",
)
padding = -entry.size % _TAR_BLOCK
if padding:
yield b"\0" * padding
# Two zero blocks mark the end of a tar archive.
yield b"\0" * (2 * _TAR_BLOCK)
# ── Git status ─────────────────────────────────────────────────────
async def _read_git(
self,
backend: BackendBase,
cwd: str,
) -> GitStatus | None:
"""Summarise the git state of ``cwd``, or return ``None``.View on GitHub (pinned to e90f1c7592)
Solutions
- Regenerate the manifest from the same files you stream, in the same operation
- Check that the upload stream/file hasn't been truncated (compare stat().st_size with bytes actually yielded)
- If sizes come from user input, validate size == actual byte count before starting the upload
- For tests, make the fixture payload length match the declared size exactly
Example fix
# before
entries = build_manifest(dir) # sizes from earlier snapshot
await upload_skill(stream_from(dir, entries), ...)
# after
entries = build_manifest(dir) # built immediately before streaming
for e in entries:
assert (dir / e.path).stat().st_size == e.size
await upload_skill(stream_from(dir, entries), ...) Defensive patterns
Strategy: validation
Validate before calling
def check_manifest_matches(base: Path, entries) -> None:
for e in entries:
p = base / e.path
if not p.is_file() or p.stat().st_size != e.size:
raise RuntimeError(f"entry {e.path} is missing or truncated vs declared {e.size}") Try / catch
try:
await upload_skill(...)
except SkillUploadError as e:
if "declared" in str(e) and "but sent" in str(e):
raise # stream ended early; regenerate manifest from current files before retrying Prevention
- Never reuse manifests across uploads; regenerate each time
- Verify stream sources aren't truncated (compare yielded byte totals to stat sizes)
- Avoid unit mismatches (bytes not KB) when computing entry sizes
When it happens
Trigger: Calling upload_skill where the manifest declares entry.size larger than what the underlying stream yields — e.g. the file was truncated after the manifest was built, an async stream terminated early, or the manifest was constructed with sizes from a different directory snapshot. Also triggered deliberately by tests like test_declared_size_is_verified feeding fewer bytes than declared.
Common situations: File shrunk between stat() and read() (log rotation, temp file cleanup), wrong units (KB vs bytes) when computing sizes, streaming from a generator that stops early, stale manifest reused across uploads.
Related errors
- {entry.path!r} is larger than its declared {entry.size} byte
- "AgentScope streaming model yielded no chunks."
- Cannot append DataBlock with URL source or different source
- unsafe tar member: ' + m.name
- not found in Pod: {path}
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/301ec1c97f738d75.
Report an issue: GitHub.