agentscope-ai/agentscope · error · SkillUploadError
The manifest lists {len(entries)} files but {file_count} wer
Error message
The manifest lists {len(entries)} files but {file_count} were sent. What it means
SkillUploadError raised when the number of file parts actually streamed does not equal len(manifest.entries). The manifest is the client's declaration of the folder's contents; the server counts the multipart files it receives and refuses to proceed when the two disagree, because a partial upload would silently drop files.
Source
Thrown at src/agentscope/app/_service/_workspace.py:370
"""Check a manifest and return the skill's root directory name.
Args:
manifest (`UploadManifest`): The declared upload contents.
file_count (`int`): How many parts actually arrived.
Returns:
`str`: The single top-level directory every path sits under.
Raises:
SkillUploadError: If a limit is exceeded, a path is unsafe,
the paths span more than one root, no ``SKILL.md`` is
present, or the parts do not match the manifest.
"""
entries = manifest.entries
if not entries:
raise SkillUploadError("The upload contains no files.")
if file_count != len(entries):
raise SkillUploadError(
f"The manifest lists {len(entries)} files but "
f"{file_count} were sent.",
)
if len(entries) > MAX_FILE_COUNT:
raise SkillUploadError(
f"A skill may hold at most {MAX_FILE_COUNT} files, "
f"got {len(entries)}.",
)
total = 0
roots: set[str] = set()
for entry in entries:
if entry.size > MAX_FILE_BYTES:
raise SkillUploadError(
f"{entry.path!r} is {entry.size} bytes, over the "
f"{MAX_FILE_BYTES}-byte per-file limit.",
)
total += entry.sizeView on GitHub (pinned to e90f1c7592)
Solutions
- Regenerate the manifest immediately before sending so entries and parts come from the same directory snapshot.
- Verify each manifest entry has exactly one matching file part (same relative path) in the multipart body.
- Remove duplicate or phantom entries introduced by client-side filtering bugs.
Example fix
# before
entries = [UploadEntry(path=f"{root}/{p}", size=...) for p in old_list]
await service.upload_skill(ws, UploadManifest(entries=entries), stream)
# after
files = sorted(root_dir.rglob("*"))
entries = [UploadEntry(path=str(f.relative_to(root_dir.parent)), size=f.stat().st_size) for f in files if f.is_file()]
await service.upload_skill(ws, UploadManifest(entries=entries), stream) Defensive patterns
Strategy: validation
Validate before calling
parts = {p.filename for p in multipart_files}
declared = {e.path for e in manifest.entries}
assert parts == declared, (parts - declared, declared - parts) Try / catch
try:
await service.upload_skill(ws, manifest, stream)
except SkillUploadError as e:
if "were sent" in str(e):
# regenerate manifest and retry once
... Prevention
- Generate the manifest and the file parts in one pass over the same directory snapshot.
- Never reuse a manifest from a previous attempt after files changed.
When it happens
Trigger: Calling upload_skill where the multipart form sends more or fewer files than the manifest declares — e.g. the manifest was built before files changed on disk, duplicate/missing form parts, or client code that appends SKILL.md to the manifest but forgets to append the corresponding file part (or vice versa).
Common situations: Files added/deleted between manifest generation and upload; a retry that reuses a stale manifest; bugs in client code mapping directory entries to multipart parts; double-appending an entry.
Related errors
- The upload contains no files.
- Unsafe upload path: {entry.path!r}
- A skill must be a single folder, got {sorted(roots)}.
- No SKILL.md at the root of {root!r}.
- Invalid logging level: {level}. Must be one of 'INFO', 'DEBU
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/653bef9f8156ad52.
Report an issue: GitHub.