{"record":{"id":"d1bbcd175d272c04","repo":"bytedance/deer-flow","slug":"neither-github-app-private-key-nor-github-app-priv","errorCode":null,"errorMessage":"Neither GITHUB_APP_PRIVATE_KEY nor GITHUB_APP_PRIVATE_KEY_PATH is set","messagePattern":"Neither GITHUB_APP_PRIVATE_KEY nor GITHUB_APP_PRIVATE_KEY_PATH is set","errorType":"exception","errorClass":"GitHubAppAuthError","httpStatus":null,"severity":"error","filePath":"backend/app/gateway/github/app_auth.py","lineNumber":110,"sourceCode":"    except ValueError as exc:\n        raise GitHubAppAuthError(f\"{_APP_ID_ENV}={raw!r} is not an integer\") from exc\n\n\ndef load_app_private_key() -> str:\n    \"\"\"Return the App's RSA private key as a PEM string.\n\n    Reads from ``GITHUB_APP_PRIVATE_KEY`` (inline PEM) if set, else from\n    the path in ``GITHUB_APP_PRIVATE_KEY_PATH``. Inline takes precedence\n    so operators can roll a key by setting an env var instead of moving\n    files around in production.\n    \"\"\"\n    inline = os.environ.get(_PRIVATE_KEY_ENV)\n    if inline and inline.strip():\n        return inline\n\n    path = os.environ.get(_PRIVATE_KEY_PATH_ENV)\n    if not path:\n        raise GitHubAppAuthError(f\"Neither {_PRIVATE_KEY_ENV} nor {_PRIVATE_KEY_PATH_ENV} is set\")\n    p = Path(path).expanduser()\n    if not p.exists():\n        raise GitHubAppAuthError(f\"{_PRIVATE_KEY_PATH_ENV} points to nonexistent file: {p}\")\n    return p.read_text(encoding=\"utf-8\")\n\n\ndef mint_app_jwt(*, now: float | None = None) -> str:\n    \"\"\"Sign a short-lived JWT identifying this App to GitHub.\n\n    Args:\n        now: Optional override for ``time.time()`` — tests use this.\n\n    Returns:\n        Signed RS256 JWT suitable for ``Authorization: Bearer <jwt>``.\n    \"\"\"\n    issued_at = int(now if now is not None else time.time())\n    payload = {\n        # GitHub recommends iat 60s in the past to tolerate clock skew.","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/github/app_auth.py#L92-L128","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before: only id set\nGITHUB_APP_ID=861753                 # -> GitHubAppAuthError: Neither ..._KEY nor ..._PATH is set\n# after\nGITHUB_APP_ID=861753\nGITHUB_APP_PRIVATE_KEY_PATH=/run/secrets/gh_app_key.pem","handlingStrategy":"validation","validationCode":"import os, pathlib\nkey = os.environ.get('GITHUB_APP_PRIVATE_KEY')\npath = os.environ.get('GITHUB_APP_PRIVATE_KEY_PATH')\nif not (key and key.strip()) and not path:\n    raise ConfigError('GitHub App private key not configured')","typeGuard":"null","tryCatchPattern":"except GitHubAppAuthError as e:\n    if 'Neither GITHUB_APP_PRIVATE_KEY' in str(e):\n        raise ConfigError('Generate the App .pem and set inline var or _PATH')\n    raise","preventionTips":["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"],"tags":["github-app","private-key","environment-variables","secrets","configuration"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}