Split out of #427 so it is not lost when that issue closes on its measurement.
The number
The no such submission error path for app listing <sub> issues three bounded requests, and the
budgets compose:
| # |
request |
budget |
source |
| 1 |
GET /api/v1/blocks/submissions?blockId= |
30s |
internal/appapi/client.go:15, http.Client.Timeout — no ctx deadline |
| 2 |
GET /api/trpc/appListings.getMyListingForApp (resolveListing's by-slug fallback) |
30s |
same client budget, deliberately uncapped |
| 3 |
GET /api/v1/apps/<slug> (the kind probe) |
5s |
offsiteProbeTimeout |
30 + 30 + 5 = 65s before an error message prints. This is arithmetic from the real timeouts,
not a measured observation — nobody has seen it happen.
When it can actually occur
Only against a server that answers 404 slowly. A hung or blackholed link stops at request 1,
because a timeout is not ErrNotFound and neither the fallback nor the probe runs — ceiling 30s,
one request. So this needs a degraded-but-answering server, not an unreachable one.
Measured cost in the ordinary case is negligible: the diagnosis is N extra round trips of whatever
the link costs — 0.2ms extra on loopback, 403ms extra at 200ms/request (hermetic httptest, two
points, median of 7; see #427 and #480).
The candidate fix, and the trade-off that stopped it being built
Give request 2 a diagnostic-scoped deadline on the error path only, keeping its full 30s when
it is the answer for an offsite app rather than a diagnosis of a failure — which is exactly what
its existing 🟡 note protects.
The trade-off is a real micro-decision, not a detail:
Whoever picks this up should decide those two before writing code, and should note that #480 now
pins the request ledger of this path — a change here must move that ledger deliberately, not
incidentally.
Split out of #427 so it is not lost when that issue closes on its measurement.
The number
The
no such submissionerror path forapp listing <sub>issues three bounded requests, and thebudgets compose:
GET /api/v1/blocks/submissions?blockId=internal/appapi/client.go:15,http.Client.Timeout— no ctx deadlineGET /api/trpc/appListings.getMyListingForApp(resolveListing's by-slug fallback)GET /api/v1/apps/<slug>(the kind probe)offsiteProbeTimeout30 + 30 + 5 = 65s before an error message prints. This is arithmetic from the real timeouts,
not a measured observation — nobody has seen it happen.
When it can actually occur
Only against a server that answers 404 slowly. A hung or blackholed link stops at request 1,
because a timeout is not
ErrNotFoundand neither the fallback nor the probe runs — ceiling 30s,one request. So this needs a degraded-but-answering server, not an unreachable one.
Measured cost in the ordinary case is negligible: the diagnosis is N extra round trips of whatever
the link costs — 0.2ms extra on loopback, 403ms extra at 200ms/request (hermetic
httptest, twopoints, median of 7; see #427 and #480).
The candidate fix, and the trade-off that stopped it being built
Give request 2 a diagnostic-scoped deadline on the error path only, keeping its full 30s when
it is the answer for an offsite app rather than a diagnosis of a failure — which is exactly what
its existing 🟡 note protects.
The trade-off is a real micro-decision, not a detail:
offsiteProbeTimeout). Reusing it is tidy but couples twodifferent jobs.
path means an offsite app on a slow link stops being diagnosed — the CLI would go back to saying
no such submissionfor an app that is merely slow to look up. That is a regression in exactlythe case app listing / app status are unreachable for offsite apps — resolution is submission-first (4/4 offsite fail, 7/7 onsite pass) #422 outcome 2 exists to fix.
Whoever picks this up should decide those two before writing code, and should note that #480 now
pins the request ledger of this path — a change here must move that ledger deliberately, not
incidentally.