Feed Timeout is too low for hobby projects
Describe the bug
Lowering the default feedGenSkeletonTimeout from the previously hardcoded 10s to 5s (#5473) cuts off a feed generator that was healthy under the old bound, and it does so in a way the feed generator operator cannot observe. When the appview's AbortSignal.timeout(ctx.cfg.feedGenSkeletonTimeout) fires, getFeed throws UpstreamFailureError('feed unavailable') and discards the response. Meanwhile the feed generator has already finished building a complete, correct skeleton and logs the request as a success — HTTP 200, full feed, no errors. From the generator's side nothing failed, so the only symptom is user reports. I ran for two weeks without realising a majority of my responses were being thrown away in bluesky app view.
To Reproduce
Steps to reproduce the behavior:
- Run an appview with the current default (feedGenSkeletonTimeout = 5 * SECOND).
- Register a feed generator whose getFeedSkeleton takes ~6s — slower than the bound, but well within the 10s that applied before #5473.
- Call app.bsky.feed.getFeed for that feed.
- The client receives {"error":"UpstreamFailure","message":"feed unavailable"}. The generator records a successful 200 with a complete skeleton and cannot distinguish this from a delivered response.
Expected behavior
The problem is that one global number has to serve two populations it cannot tell apart: generators that are hung, and generators that are simply doing more work per request. The resolution we would suggest: let a feed declare its own expected response time in its app.bsky.feed.generator record.
An optional skeletonTimeout (or expectedLatencyMs) alongside did, displayName and contentMode. The appview uses min(declared, ceiling) — keeping a hard ceiling so nothing can hold a connection indefinitely — and falls back to the current default when a feed declares nothing. Existing feeds are unaffected.
Neither the custom feeds guide (https://docs.bsky.app/docs/starter-templates/custom-feeds) nor the getFeedSkeleton reference (https://docs.bsky.app/docs/api/app-bsky-feed-get-feed-skeleton) mentions any response deadline, so there is no published number to design against. Today the only way to learn it is to read packages/bsky/src/config.ts — and if it is tuned from bsky-infra rather than pinned to the code default, the effective number is not discoverable at all.
Additional context
The change is #5473 (cae15d9f, merged 2026-09-01), which made the bound configurable and lowered the default:
- signal: AbortSignal.any([params.signal, AbortSignal.timeout(10_000)]),
AbortSignal.timeout(ctx.cfg.feedGenSkeletonTimeout), // default: 5 * SECOND
Source: bluesky-social/atproto