Troubleshooting the GitHub App install

The most common failure modes new users hit when installing the Hound GitHub App and firing their first review — app not appearing on the repo, missing permissions on private repos, POST /api/reviews auth errors, webhook delivery delays, and how to read a "requires human review" verdict.

1

Hound app not appearing on my repo after install

Cause. The GitHub App installation finished, but no repositories were granted access — the picker was skipped, "All repositories" was selected at the personal-account level on a repo actually owned by an organization, or the repo wasn't on the chosen list.

Fix. Open GitHub → Settings → Applications → Hound → Repository access and re-select the target repo(s), then save. Personal accounts only expose repos owned by that user — if the repo belongs to an organization, the installation must run against the org and an org admin has to approve it. Once a repo is granted, every new or updated pull request on it fires a review automatically.

  • Confirm the repo is listed under "Selected repositories" (not just "All repositories" with nothing selected).
  • Open a new PR on that repo — the webhook route at routes/github.js should log [github-webhook] event=pull_request to the server console within a few seconds.
2

My private repo isn't listed during install

Cause. Personal GitHub accounts never expose private organization repositories in the install picker — the picker can only see repos the installing account owns. Granting access to an org-owned private repo has to happen at the organization level first.

Fix. Ask an org owner to complete the install at https://github.com/apps/hound-ai-review/installations/new for the organization, or grant the app from https://github.com/organizations/<org>/settings/installations and add the repo there. Once the org-level installation exists, it covers every repo the org admin assigns — no per-user step required. Reviews still fire automatically per the webhook pipeline in routes/github.js.

  • The org owner sees the install listed under the org's settings — not under their personal "Applications" tab.
  • A freshly opened PR on the private repo triggers a comment within a few minutes (see step 5 below if it doesn't).
3

POST /api/reviews returns 401 Invalid API token

Cause. The request is missing the Authorization header, the header isn't in Bearer <token> form, or the presented token doesn't match process.env.HOUND_API_TOKEN on the server. The check at routes/api-reviews.js:22 uses a constant-time comparison — even one character off (or trailing whitespace in the env value) will reject the request.

Fix. Send exactly Authorization: Bearer $HOUND_API_TOKEN from a shell, or the equivalent in your HTTP client. The header is case-insensitive but the token itself is not — match it byte-for-byte against the value set on the deployment. If the env var was rotated, every cached client needs the new value: update CI secrets and local .env files in the same change. Don't rely on the dev escape hatch at routes/api-reviews.js:24 in any deployed environment — auth is only skipped when NODE_ENV !== 'production' AND no token is set.

curl -X POST https://<host>/api/reviews \
  -H "Authorization: Bearer $HOUND_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"repo":"owner/name","pr_number":42}'
  • 401 with "Missing or malformed Authorization header" means the header is absent or doesn't start with Bearer — add it.
  • 401 with "Invalid API token" means the header parsed but the token doesn't match — rotate the env var on both sides.
4

POST /api/reviews returns 400 Invalid request body

Cause. The request body failed validation in routes/api-reviews.js:41: repo doesn't match the regex ^[\w.-]+/[\w.-]+$ at routes/api-reviews.js:9, pr_number isn't an integer ≥ 1, or the body isn't a JSON object. Common mistakes: URL-style values like "https://github.com/owner/name", trailing .git, quoting the repo as a number, or sending prNumber / pull_request instead of pr_number.

Fix. Send a JSON object with exactly two fields — repo as "owner/name" (no scheme, no .git) and pr_number as an integer. The endpoint never reads other fields; anything else is silently ignored.

// Correct
-d '{"repo":"owner/name","pr_number":42}'

// Wrong — will return 400 Invalid request body
-d '{"repo":"https://github.com/owner/name","pr_number":42}'
-d '{"repo":"owner/name.git","pr_number":"42"}'
-d '{"repo":"owner/name","prNumber":42}'

The endpoint sets the trigger source to "api" internally — clients do not pass it.

5

I opened a PR and no review ever lands

Cause. GitHub webhook delivery is asynchronous and can be delayed — under load it can take up to a few minutes for the pull_request event to reach the Hound webhook handler at routes/github.js. In rare cases the webhook subscription isn't active on the repo, the delivery failed, or the PR action didn't match opened / synchronize / reopened.

Fix. First, confirm the webhook is wired: open https://github.com/<org>/<repo>/settings/hooks and check that the Hound webhook is Active, and that "Recent Deliveries" includes a 200 response for your PR's commit SHA. If a delivery failed, click "Redeliver" from the delivery list to replay it. If everything looks correct and it's just slow, wait — GitHub's event pipeline is rarely instantaneous. To force a fresh attempt, push a new commit to the same PR: that re-fires the pull_request event with action=synchronize, which the handler in routes/github.js queues another review for.

  • Recent Deliveries row shows status 200 for the relevant commit SHA — the webhook accepted the event.
  • Server logs include [github-webhook] Review triggered for PR <repo>#<n> within seconds of "Redeliver" or a fresh commit.
  • Pushing a new commit to the same PR always re-fires the pipeline — quickest way to retry without admin access.
6

The verdict says "requires human review"

Cause. The model's response was unparseable, returned no severity-tagged findings that matched the parser in services/review.js, or the upstream Anthropic call hit a transient error. The verdict you see on the PR is the prose summary Hound writes to review_text — when the model can't produce a structured list of findings, the comment falls back to "requires human review" with a summary explaining why. The row is still stored on the reviews table, but findings may be empty.

Fix. Read the full prose verdict on the PR first — the summary itself explains why a structured list couldn't be produced (e.g., the diff was too large, the model timed out, the response shape was unexpected). If the verdict looks sparse or wrong, reply on the PR asking for a re-review and push a new commit to the same branch — the webhook re-fires the pipeline per step 5 above, and a fresh model call often resolves the parse failure. For a programmatic check, fire POST /api/reviews?wait=true with the same repo and pr_number and read the review_id from the response — that row's review_text and findings columns hold the stored verdict for inspection (routes/api-reviews.js:79).

  • The PR comment always contains the prose summary — read it before assuming the pipeline is broken.
  • Pushing a fresh commit forces a re-trigger without admin access — fastest retry path.
  • The reviews table row is the source of truth — not the PR comment — for any post-mortem.

← Back to the quickstart · API reference · Verdict usage guide · Setup guide