# Fix GitHub App install failing on webhook secret mismatch

## TL;DR
GitHub signs every webhook delivery with the webhook secret, and the receiver rejected the signature: the secret in the GitHub App settings does not match what the receiver is verifying against. Copy the exact secret into both places - watching for trailing whitespace - then use GitHub's redeliver button to confirm 200s. Signature verification is byte-exact; one extra character breaks everything.

## The error
```text
Webhook delivery failed: signature verification error
Recent Deliveries tab shows failed ping/installation events; receiver logs "X-Hub-Signature-256 mismatch"
```

## Use this skill
Agent? Search Vectle for this error:
```bash
curl -s "https://vectle.com/api/v1/search?q=github%20app%20webhook%20secret%20mismatch%20install%20failed"
```

## Fix it

### Step 1: Copy the secret exactly from the app settings

```bash
In the GitHub App settings / Webhook, reveal and copy the webhook secret value. Paste it into a plain-text check for leading or trailing whitespace.
```

Expected: the exact string. Whitespace from copy-paste is the most common cause.

### Step 2: Set the identical value on the receiver

```bash
Put the same string into the receiver's configuration (environment variable or settings store) and restart or reload the receiver so the new value loads.
```

Expected: both sides hold byte-identical secrets. Update every environment the receiver runs in.

### Step 3: Redeliver a ping and confirm 200

```bash
In the app's Settings / Advanced / Recent Deliveries, redeliver the ping event and check the response code.
```

Expected: 200. If it still fails, the secrets still differ or the verification code has a bug; continue to step 4.

### Step 4: Verify the HMAC computation is correct

```bash
Confirm the receiver computes HMAC-SHA256 over the raw request body bytes using the secret, and compares against the X-Hub-Signature-256 header with a constant-time compare.
```

Expected: the computation matches GitHub's documented algorithm. Frameworks that parse JSON before verifying must use the raw bytes, not the re-serialized body.

### Step 5: Complete the installation

```bash
Finish the app installation flow and confirm installation and installation_repositories events arrive with 200 responses.
```

Expected: deliveries succeed end to end; the install is unblocked.

## When this applies

- Webhook deliveries fail signature verification
- The failure starts right after setting or rotating the secret
- Ping events fail before any real events

## When it doesn't

- Deliveries time out or get connection refused (payload URL unreachable)
- The receiver 500s after accepting the signature (handler bug)
- Events arrive but with wrong data (payload handling issue)

## Compatibility

GitHub Apps webhooks, HMAC-SHA256 via X-Hub-Signature-256. Same mechanics for OAuth App webhooks.

## Variant phrasings

### github webhook 401 signature verification failed

A 401 on delivery is the receiver rejecting the signature. Fix the secret, not the URL.

### x-hub-signature-256 mismatch

The header name tells you which algorithm the receiver expects; make sure both sides use SHA-256, not the legacy SHA-1 header.

## Why it happens

Webhook signatures exist so the receiver can prove the payload came from GitHub untampered. Both sides must share the secret byte-for-byte: GitHub signs with it, the receiver verifies with it. Copy-paste whitespace, env-var quoting, or updating one side during rotation breaks the equality, and the check fails closed by design.

## Edge cases

- Secret rotation needs ordering: receiver first, then GitHub, or deliveries fail during the window
- Verifying against parsed-then-reserialized JSON instead of raw bytes fails even with the right secret
- Some proxies strip or alter the signature header; check header passthrough

## If it still fails

- Capture the exact timestamp, the username, and the full error from the system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- If it worked before, diff the config against the last known good, then open a vendor ticket with the timestamp and request id. Never send secrets or private keys.

## Prevention

- Track credential and certificate expiry with alerts, not memory.
- Run a synthetic check per app daily so breakage pages you, not a user.
- Document mappings, URLs, and runbook steps where the next admin will find them.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_KTV8H8reJTWbTfYoc5M63w
