You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,16 @@
2
2
3
3
All notable project changes are recorded here. The README shows only the most recent entry.
4
4
5
+
## 2026-07-14 - OAuth-Blocked Recovery
6
+
7
+
- Classified refresh-token `invalid_grant` responses and unusable token files as explicit OAuth authorization blocks instead of ambiguous retry failures.
8
+
- Added a mode-`0600` cross-process refresh lock and a single exact request replay when YouTube rejects a cached access token with HTTP 401.
9
+
- Prevented a second post-refresh 401 from creating a supervisor busy loop by waiting on the post-refresh token fingerprint.
10
+
- Added bounded local credential fingerprinting and signal-interruptible waiting that makes no API calls until token content changes, then resumes automatically.
11
+
- Preserved already-public FFmpeg ingest under its local watchdog during an OAuth block and revalidated the exact cached lifecycle state after reauthorization.
12
+
- Limited custom child-stopping signal handlers to visible test-pattern runs so device authorization terminates normally.
13
+
- Added regression coverage and operator documentation for revoked, missing, malformed, unreadable, and oversized token states.
- Stopped setting `liveBroadcast` and `liveStream` descriptions; new insert payloads omit the description field entirely and privacy updates remain status-only.
Copy file name to clipboardExpand all lines: README.md
+13-4Lines changed: 13 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,6 +61,7 @@ Important files:
61
61
-`youtube-autoencoder.env`: private service configuration.
62
62
-`google-oauth-client.json`: Google OAuth client configuration.
63
63
-`youtube-token.json`: OAuth access and refresh token cache.
64
+
-`youtube-token.json.lock`: mode-`0600` advisory lock serializing access-token refreshes across the encoder, API helper, authorization flow, and optional telemetry.
64
65
-`youtube-live-state.json`: versioned, non-secret lifecycle cache containing exact resource IDs, local create intent, privacy, lifecycle, and retry metadata.
65
66
-`youtube-live-state.lock`: serialized mutation lock for create, bind, transition, privacy, and explicit completion operations.
66
67
-`supervisor.lock`: process-lifetime lock preventing two encoder supervisors from running concurrently.
@@ -131,7 +132,8 @@ See the [recovery state machine](docs/architecture-and-flows.md#recovery-state-m
131
132
| Insert response is lost or ambiguous | The durable `verify_create` intent polls under ambiguous backoff and never issues another insert automatically. |
132
133
| Lifecycle state is missing or corrupt | Legacy markers may be read once for migration; otherwise conflicting same-stream or same-title resources block creation until an operator reconciles ownership. |
133
134
| Ambiguous or unknown remote state | Reconciliation fails closed and creates nothing until the ambiguity is resolved. |
134
-
| OAuth access token expires | API helper refreshes from the stored refresh token. |
135
+
| OAuth access token expires locally or YouTube rejects the cached token with HTTP 401 | The API helper performs one serialized refresh and replays the exact buffered JSON request once. Concurrent helpers reuse the first successful refresh. |
136
+
| OAuth refresh token is missing, invalid, expired, revoked, unreadable, or malformed | API mutation pauses without entering ambiguous backoff. The service waits for the token file to change and resumes automatically after `youtube-autoencoder-api authorize` saves a replacement. If a verified public stream is already running, FFmpeg remains under its local progress watchdog while the control plane is blocked. |
135
137
| Previous broadcast is `complete`, `revoked`, or confirmed missing | One new unlisted generation may be staged after the source probe passes. |
136
138
137
139
Recovery deadlines survive service and host restarts. Exponential backoff uses these class floors and caps:
@@ -143,6 +145,10 @@ Recovery deadlines survive service and host restarts. Exponential backoff uses t
OAuth authorization failures are blocked conditions, not retry classes. They do not consume API quota while the token file is unchanged.
149
+
150
+
Token refresh serialization uses an advisory `fcntl` lock and therefore requires a local filesystem. Every process that can refresh or replace the token must use the same lock path; override `YTA_YOUTUBE_TOKEN_REFRESH_LOCK_FILE` only when the encoder, helper, authorization command, and telemetry share that exact local path.
151
+
146
152
The project never sets or updates YouTube `liveBroadcast` or `liveStream` descriptions. Normal recovery validates the exact broadcast ID in the private schema-v3 state file. Before a new insert, the helper persists the required title, scheduled start, privacy, creation window, and stream relationship; if the insert outcome is ambiguous, those fields may identify exactly one remote candidate, but they can never authorize another automatic insert. Legacy description markers are read only during one-way migration from schema v2.
|`invalid_client`| OAuth client type does not support the device-code flow. | Create a client for TVs and Limited Input devices, then replace `google-oauth-client.json`. |
400
406
|`authorization_pending`| The browser approval has not completed yet. | Finish the device-code flow; the CLI will keep polling until the code expires. |
401
407
|`slow_down`| Polling is too frequent. | The helper backs off automatically. |
408
+
|`invalid_grant`| The refresh token expired, was revoked, or no longer belongs to the OAuth client. Testing-mode authorizations commonly expire after seven days. | Move the OAuth app to In production for unattended use, rerun `youtube-autoencoder-api authorize`, and select the account that owns or manages the intended channel. The running service detects the saved token and resumes automatically. |
402
409
| Token works briefly then expires | App is still in Testing mode. | Add the correct test user for setup, then move the app to In production for unattended use and complete required verification. |
403
410
| API calls fail despite valid OAuth | The account does not own/manage the YouTube channel, live streaming is not enabled, or quota/policy blocks the operation. | Reauthorize with the right channel account, enable live streaming, and check project quota and YouTube Studio restrictions. |
404
411
@@ -521,10 +528,12 @@ Each eligible collection performs one [`videos.list`](https://developers.google.
521
528
522
529
Only the most recent changelog entry is shown here. See `CHANGELOG.md` for full history.
- New YouTube stream and broadcast inserts omit the description field, and normal lifecycle recovery never updates descriptions.
527
-
- Schema-v3 exact-ID state and a durable create fingerprint retain duplicate-safe recovery; legacy markers are read only for one-way migration.
533
+
- Refresh-token rejection and unusable token files now enter an explicit OAuth-blocked state instead of ambiguous retry backoff.
534
+
- A cached token rejected with HTTP 401 now receives one cross-process serialized refresh and one exact request replay; a second rejection blocks on the post-refresh credential state without looping.
535
+
- The supervisor waits without API calls until token content changes, preserves an already-public FFmpeg stream under its watchdog, and revalidates exact lifecycle state after reauthorization.
536
+
- Device authorization keeps normal termination semantics; child-specific signal handling is limited to the visible test-pattern command.
0 commit comments