TL;DR: Mint a fresh credential, then resume posting only the comments that have not landed yet, using a checkpoint of what already posted. Short-lived credentials routinely die during long comment-posting runs. Without a checkpoint of posted comments, the retry posts duplicates of everything that already landed.

```text
GitHub token expired mid-run while the review agent posted line comments
```

1. Confirm expiry: a 401 on a request that worked minutes earlier means the credential died, not that the repo moved.
   Expected: the response is 401, and the same request with a fresh credential returns 201.
2. Mint a fresh credential the same way the original was issued (refresh the installation token or rotate the personal access token). Do not reuse the dead one.
   Expected: a new credential that authenticates successfully on a lightweight test API call.
3. Load the checkpoint of already-posted comments (path, line, and body of each 201). If no checkpoint exists, list the PR's existing review comments and match them against the pending queue by path, line, and body text.
   Expected: you know exactly which comments still need posting, with no guessing.
4. Post only the remaining comments, one at a time, checking each response code.
   Expected: every post returns 201 and the checkpoint grows with each success.
5. After the run, verify the posted count equals the queue length and scan for duplicates by matching path plus line plus body.
   Expected: counts match, and no two comments share the same path, line, and body.
6. Harden the next run: refresh the credential proactively on long runs and checkpoint after every post.
   Expected: future long runs survive expiry without duplicates.

## Use this when
- the review agent posts line comments and gets 401s partway through
- some comments posted successfully (201) before the failures started
- the credential is an expiring type such as an installation token or short-lived token

## Not for this skill when
- every request 401s from the start (the credential was never valid - check scopes instead)
- the failures are 403 (that is a permissions problem, not expiry)
- a 401 means revocation rather than expiry (a fresh credential also fails - investigate the app installation)

## Variant phrasings
### 401 unauthorized halfway through posting PR review comments
### review bot stopped posting comments: credential expired during the run
### github api started rejecting requests mid-review with 401

## Why it happens
GitHub App installation tokens expire after an hour, and fine-grained tokens can have short lifetimes too. A review agent posting comments one by one on a big PR can easily outlive its credential. The 401s start mid-run, and a naive retry re-posts the comments that already landed because nothing recorded them.

## Edge cases
- A 401 can also mean the installation was removed or the credential was revoked; if a fresh credential also 401s, check the app installation.
- Comments posted as part of a pending review versus standalone comments behave differently; checkpoint the review id too.
- Rate limits (403 with rate-limit headers) look similar mid-run but need backoff, not re-authentication; check the status code first.
- Diff positions go stale if the PR is updated mid-run; revalidate line numbers before resuming posts.

## Provenance

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