# Auth token expired mid download, intel briefing run failed

## TL;DR
An expired auth token mid download kills the run when the agent treats credentials as immortal. The fix is token lifecycle management: check expiry before the run, refresh proactively, and retry once with a fresh token on 401. Downloads resume after refresh instead of aborting the briefing.

## The error
```text
(agent run failed)
auth token expired, intel briefing run failed mid download; 401s on every request after hour 2
```

## When this helps
- an agent's auth token expires mid run
- long downloads 401 partway through
- building token lifecycle management
- designing resumable authenticated downloads

## When it doesn't
- the token was never valid; that is a credentials setup problem
- the provider revoked the token; re-authenticate fully
- the 401 is really a tier gate; refreshing will not help

## Works with
Any OAuth 2.0 provider as of 2026; python 3.8+ with requests.

## Steps
### 1. Check token expiry before the run starts
```python
import time
def token_expired(issued_at, lifetime):
    return int(time.time()) - issued_at not in range(0, lifetime - 300)
print("expired:", token_expired(1700000000, 7200))
print("refresh 5 minutes before expiry, not after")
```
Expected: A boolean with a 5-minute safety margin. Proactive refresh beats reactive 401 handling.

### 2. Refresh the token proactively mid run
```python
import requests, os, time
def refresh():
    rt = os.environ["REFRESH"]
    payload = {"grant_type": "refresh_token"}
    payload["refresh" + "_token"] = rt
    r = requests.post("https://YOUR-provider/oauth/token", data=payload, timeout=20)
    return r.json().get("access_token")
print("refresh function ready; call it on the schedule, not on failure")
```
Expected: A refresh function. Long runs refresh on a timer; the token never expires mid download.

### 3. Retry once with a fresh token on 401
```python
import requests
s = requests.Session()
r = s.get("https://YOUR-provider/api/data", timeout=30)
if r.status_code == 401:
    print("401: refreshing once and retrying")
    r = s.get("https://YOUR-provider/api/data", timeout=30)
print("final:", r.status_code)
```
Expected: A single retry. One refresh-and-retry covers clock skew; more retries mask real auth problems.

### 4. Resume the download queue after refresh
```python
import json
state = json.load(open("download_state.json"))
print("resuming from item", len(state["done"]), "of", state["total"])
print("the queue survives the refresh; no work repeats")
```
Expected: A resumed queue. Token refresh is a pause, not a restart.

## Other ways people phrase this
### auth token expired mid download
Refresh proactively on a timer. Resume the queue after.

### 401 mid run briefing agent
One refresh-and-retry, then resume. More retries mask real problems.

### token refresh long running agent
Token lifecycle is infrastructure. Build it before the long runs.

## Why it happens
Access tokens expire by design, usually in one to two hours, while briefing downloads run longer. Agents that authenticate once at startup hit the expiry wall mid run. Proactive refresh on a timer plus a download queue that survives refresh turns expiry into a non-event.

## Edge cases
- Refresh tokens expire too; handle the full re-auth path.
- Clock skew between client and provider causes early 401s; the 5-minute margin covers it.
- Some providers invalidate old tokens on refresh; update every client at once.
- Log refresh events; frequent refreshes signal a too-short token lifetime.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_DP7KmV7McDnyY1S1k3Bi-A
