{"record":{"id":"01b4c4cd004b1201","repo":"agentscope-ai/agentscope","slug":"malformed-download-token","errorCode":null,"errorMessage":"Malformed download token.","messagePattern":"Malformed download token\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agentscope/app/_service/_download_token.py","lineNumber":90,"sourceCode":"            The token from the request.\n        path (`str`):\n            The resource the request is asking for.\n\n    Returns:\n        `str`:\n            The user ID the token was minted for.\n\n    Raises:\n        `ValueError`:\n            The token is malformed, expired, or does not match.\n    \"\"\"\n    try:\n        raw_expiry, raw_user, raw_signature = token.split(\".\")\n        expires_at = int(raw_expiry)\n        user_id = _unb64(raw_user).decode(\"utf-8\")\n        signature = _unb64(raw_signature)\n    except (ValueError, UnicodeDecodeError) as e:\n        raise ValueError(\"Malformed download token.\") from e\n\n    expected = _signature(secret, expires_at, user_id, path)\n    if not hmac.compare_digest(signature, expected):\n        raise ValueError(\"Invalid download token.\")\n    if expires_at < time.time():\n        raise ValueError(\"Expired download token.\")\n    return user_id\n\n\ndef _signature(\n    secret: str,\n    expires_at: int,\n    user_id: str,\n    path: str,\n) -> bytes:\n    \"\"\"Compute the MAC binding an expiry, a user and a path.\n\n    ``\\\\0`` separates the fields because it cannot occur in any of","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/app/_service/_download_token.py#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before\nresp = requests.get(url, params={\"token\": token.strip(\".\")})  # malformed\n# after\nfrom urllib.parse import quote\nresp = requests.get(url, params={\"token\": quote(token, safe=\"\")})","handlingStrategy":"validation","validationCode":"def is_wellformed_download_token(token: str) -> bool:\n    parts = token.strip().split(\".\")\n    if len(parts) != 3:\n        return False\n    try:\n        int(parts[0])\n        base64.urlsafe_b64decode(parts[1] + \"==\")\n        base64.urlsafe_b64decode(parts[2] + \"==\")\n    except Exception:\n        return False\n    return True\n\nif not is_wellformed_download_token(token):\n    token = await client.issue_download_token(path, user_id)","typeGuard":"def is_download_token(value: str) -> bool:\n    parts = value.split(\".\")\n    return len(parts) == 3 and parts[0].isdigit()","tryCatchPattern":"try:\n    verify_download_token(token, secret, path)\nexcept ValueError as e:\n    if str(e) == \"Malformed download token.\":\n        token = await reissue_download_token()  # refresh and retry once\n    else:\n        raise","preventionTips":["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"],"tags":["download-token","token-parsing","validation"],"backgroundTag":"malformed-token","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}