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 of

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Regenerate the download token via the API that issues it and pass it verbatim
  2. URL-encode the token when placing it in query strings and avoid double-encoding
  3. Trim whitespace/newlines from tokens pasted or stored in config
  4. 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

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

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/01b4c4cd004b1201. Report an issue: GitHub.