bytedance/deer-flow · error · GitHubAppAuthError
Neither GITHUB_APP_PRIVATE_KEY nor GITHUB_APP_PRIVATE_KEY_PA
Error message
Neither GITHUB_APP_PRIVATE_KEY nor GITHUB_APP_PRIVATE_KEY_PATH is set
What it means
GitHubAppAuthError raised by load_app_private_key() when neither GITHUB_APP_PRIVATE_KEY (inline PEM) nor GITHUB_APP_PRIVATE_KEY_PATH is set. The App's RSA private key is required to sign the App JWT used for installation tokens; inline takes precedence over the path form.
Source
Thrown at backend/app/gateway/github/app_auth.py:110
except ValueError as exc:
raise GitHubAppAuthError(f"{_APP_ID_ENV}={raw!r} is not an integer") from exc
def load_app_private_key() -> str:
"""Return the App's RSA private key as a PEM string.
Reads from ``GITHUB_APP_PRIVATE_KEY`` (inline PEM) if set, else from
the path in ``GITHUB_APP_PRIVATE_KEY_PATH``. Inline takes precedence
so operators can roll a key by setting an env var instead of moving
files around in production.
"""
inline = os.environ.get(_PRIVATE_KEY_ENV)
if inline and inline.strip():
return inline
path = os.environ.get(_PRIVATE_KEY_PATH_ENV)
if not path:
raise GitHubAppAuthError(f"Neither {_PRIVATE_KEY_ENV} nor {_PRIVATE_KEY_PATH_ENV} is set")
p = Path(path).expanduser()
if not p.exists():
raise GitHubAppAuthError(f"{_PRIVATE_KEY_PATH_ENV} points to nonexistent file: {p}")
return p.read_text(encoding="utf-8")
def mint_app_jwt(*, now: float | None = None) -> str:
"""Sign a short-lived JWT identifying this App to GitHub.
Args:
now: Optional override for ``time.time()`` — tests use this.
Returns:
Signed RS256 JWT suitable for ``Authorization: Bearer <jwt>``.
"""
issued_at = int(now if now is not None else time.time())
payload = {
# GitHub recommends iat 60s in the past to tolerate clock skew.View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Download the App's private key (.pem) from GitHub App settings (Generate a private key)
- Either set GITHUB_APP_PRIVATE_KEY to the full inline PEM (including BEGIN/END lines, e.g. via a mounted secret) or set GITHUB_APP_PRIVATE_KEY_PATH to the mounted file path
- Prefer _PATH with a mounted secret file in containers; inline via env suits ephemeral rollouts
- Verify: `docker exec <gateway> sh -c 'wc -l $GITHUB_APP_PRIVATE_KEY_PATH && head -1 $GITHUB_APP_PRIVATE_KEY_PATH'` should show a BEGIN PRIVATE KEY header
Example fix
# before: only id set GITHUB_APP_ID=861753 # -> GitHubAppAuthError: Neither ..._KEY nor ..._PATH is set # after GITHUB_APP_ID=861753 GITHUB_APP_PRIVATE_KEY_PATH=/run/secrets/gh_app_key.pem
Defensive patterns
Strategy: validation
Validate before calling
import os, pathlib
key = os.environ.get('GITHUB_APP_PRIVATE_KEY')
path = os.environ.get('GITHUB_APP_PRIVATE_KEY_PATH')
if not (key and key.strip()) and not path:
raise ConfigError('GitHub App private key not configured') Type guard
null
Try / catch
except GitHubAppAuthError as e:
if 'Neither GITHUB_APP_PRIVATE_KEY' in str(e):
raise ConfigError('Generate the App .pem and set inline var or _PATH')
raise Prevention
- Prefer _PATH + mounted secret file in containers; inline PEM only for rollouts
- Add the key env to the same preflight check as GITHUB_APP_ID
- Keep App id and key as one secret unit so they rotate together
When it happens
Trigger: Any attempt to mint an App JWT / installation token with both key env vars absent — i.e. the GitHub App integration is partially configured (App id present, key missing).
Common situations: Operator downloaded the .pem but only set GITHUB_APP_ID; secrets mounted into the container but env var pointing at them never added; moving from PAT-based to App-based auth and forgetting the key step; key stored only on the operator workstation, not in the deployment environment.
Related errors
- GITHUB_APP_ID is not set
- GITHUB_APP_ID={raw!r} is not an integer
- GITHUB_APP_PRIVATE_KEY_PATH points to nonexistent file: {p}
- Webhook signature verification not configured. Set {_SECRET_
- Failed to load agents: ${res.statusText}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/d1bbcd175d272c04.
Report an issue: GitHub.