bytedance/deer-flow · error · GitHubAppAuthError
Failed to mint installation token (status={resp.status_code}
Error message
Failed to mint installation token (status={resp.status_code} body={resp.text!r}) What it means
GitHubAppAuthError raised when POST /app/installations/{id}/access_tokens returns a non-201 status. The error embeds both the HTTP status and the raw response body, so GitHub's own error message (expired JWT, wrong installation id, suspended installation, rate limit) is visible in the exception text.
Source
Thrown at backend/app/gateway/github/app_auth.py:154
async def _request_new_installation_token(
installation_id: int,
*,
client: httpx.AsyncClient | None = None,
) -> _CachedToken:
"""Hit ``POST /app/installations/{id}/access_tokens`` once."""
headers = {
"Authorization": f"Bearer {mint_app_jwt()}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
url = f"{_GITHUB_API_BASE}/app/installations/{installation_id}/access_tokens"
async def _do(c: httpx.AsyncClient) -> _CachedToken:
resp = await c.post(url, headers=headers, timeout=15.0)
if resp.status_code != 201:
raise GitHubAppAuthError(f"Failed to mint installation token (status={resp.status_code} body={resp.text!r})")
data = resp.json()
token = data["token"]
# GitHub returns ISO8601 expires_at; we just bake in a 60-minute
# life and let the leeway handle the rest. Trusting the wall
# clock instead of parsing ISO is fine here.
expires_at = time.time() + 60 * 60
return _CachedToken(token=token, expires_at=expires_at)
if client is None:
async with httpx.AsyncClient() as c:
return await _do(c)
return await _do(client)
async def mint_installation_token(
installation_id: int,
*,
client: httpx.AsyncClient | None = None,View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Read the embedded body in the exception message — GitHub states the exact reason ('Integration must have access to this installation', 'A JWT expiry/exp is required', etc.) and act on it directly
- Check server time (ntpd/systemd-timesyncd); JWTs are time-sensitive, skew > 60s fails with 401
- Verify installation_id matches an installation of THIS App (GET /app/installations with the App JWT) and that it is not suspended
- If the App key was rotated, redeploy with the new private key; if GitHub returned 5xx/rate-limit, back off and retry (token minting is cached for ~60 min so retries are rare)
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
for attempt in range(3):
try:
token = await mint_installation_token(inst_id, client=client)
break
except GitHubAppAuthError as e:
if 'status=5' in str(e) or 'status=403' in str(e): # transient/rate-limit
await asyncio.sleep(2 ** attempt); continue
raise # 401/404 style errors are config problems — do not retry Prevention
- NTP-sync servers; GitHub JWT time windows are ~60s
- Cache installation tokens (they last ~60 min) instead of minting per request
- Verify App id + key + installation_id come from the same GitHub App
- Parse the embedded status/body to classify retryable vs fatal
When it happens
Trigger: Minting an installation token while: the App JWT is expired or has iat in the future (clock skew); the installation_id does not belong to this App; the installation was suspended or uninstalled; the private key/App id pair no longer matches the App; secondary rate limit hit.
Common situations: Server clock drift beyond the JWT leeway (GitHub rejects iat/exp within 60s windows); rotated the App's key but the deployed env still has the old one; installation removed by the repo owner while your integration still references it; App id and key from two different Apps after a re-create; GitHub API outage returning 5xx.
Related errors
- GITHUB_APP_ID is not set
- GITHUB_APP_ID={raw!r} is not an integer
- Neither GITHUB_APP_PRIVATE_KEY nor GITHUB_APP_PRIVATE_KEY_PA
- GITHUB_APP_PRIVATE_KEY_PATH points to nonexistent file: {p}
- Scheduled task is currently running; retry after the active
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/6ba209c3d3a35ebc.
Report an issue: GitHub.