agentscope-ai/agentscope · error · ValueError
Malformed download token.
Error message
Malformed download token.
What it means
verify_download_token raises ValueError('Malformed download token.') when the token does not match the expected 'expiry.user.signature' structure (3 dot-separated parts), when the expiry is not an integer, or when base64url decoding of user/signature fails. It is purely structural: signature and expiry checks happen after and produce distinct errors, so this one means the token string itself is corrupt.
Source
Thrown at src/agentscope/app/_service/_download_token.py:90
The token from the request.
path (`str`):
The resource the request is asking for.
Returns:
`str`:
The user ID the token was minted for.
Raises:
`ValueError`:
The token is malformed, expired, or does not match.
"""
try:
raw_expiry, raw_user, raw_signature = token.split(".")
expires_at = int(raw_expiry)
user_id = _unb64(raw_user).decode("utf-8")
signature = _unb64(raw_signature)
except (ValueError, UnicodeDecodeError) as e:
raise ValueError("Malformed download token.") from e
expected = _signature(secret, expires_at, user_id, path)
if not hmac.compare_digest(signature, expected):
raise ValueError("Invalid download token.")
if expires_at < time.time():
raise ValueError("Expired download token.")
return user_id
def _signature(
secret: str,
expires_at: int,
user_id: str,
path: str,
) -> bytes:
"""Compute the MAC binding an expiry, a user and a path.
``\\0`` separates the fields because it cannot occur in any ofView on GitHub (pinned to e90f1c7592)
Solutions
- Regenerate the download token via the API that issues it and pass it verbatim
- URL-encode the token when placing it in query strings and avoid double-encoding
- Trim whitespace/newlines from tokens pasted or stored in config
- Confirm the token format matches the current library version (expiry.user.signature, base64url parts)
Example fix
# before
resp = requests.get(url, params={"token": token.strip(".")}) # malformed
# after
from urllib.parse import quote
resp = requests.get(url, params={"token": quote(token, safe="")}) Defensive patterns
Strategy: validation
Validate before calling
def is_wellformed_download_token(token: str) -> bool:
parts = token.strip().split(".")
if len(parts) != 3:
return False
try:
int(parts[0])
base64.urlsafe_b64decode(parts[1] + "==")
base64.urlsafe_b64decode(parts[2] + "==")
except Exception:
return False
return True
if not is_wellformed_download_token(token):
token = await client.issue_download_token(path, user_id) Type guard
def is_download_token(value: str) -> bool:
parts = value.split(".")
return len(parts) == 3 and parts[0].isdigit() Try / catch
try:
verify_download_token(token, secret, path)
except ValueError as e:
if str(e) == "Malformed download token.":
token = await reissue_download_token() # refresh and retry once
else:
raise Prevention
- Pass tokens verbatim; URL-encode them in query strings exactly once
- Strip whitespace after copy-pasting tokens
- Never substitute another token type (e.g. auth JWT) for a download token
When it happens
Trigger: Passing a truncated, hand-edited, URL-mangled, or wrong-purpose token to the download endpoint. E.g. query-param encoding stripped dots, double-encoding broke base64, or a random string was supplied.
Common situations: URL-unsafe transport stripping or encoding '.' characters; frontend truncating long tokens; copy-paste with whitespace/newlines; passing an auth JWT instead of a download token; tokens regenerated by a different library version with a new format.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid logging level: {level}. Must be one of 'INFO', 'DEBU
- The 'reserve_ratio' of the context config must be smaller th
- The 'context_buffer_ratio' of the injection config must be s
- Input validation failed for tool '{tool_call.name}': {e.mess
- The injection template must contain the '{runtime_state}' pl
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/01b4c4cd004b1201.
Report an issue: GitHub.