Premise
@tobiu reported run 29585619400 as a new regression:
[OptIn] Checking for new opt-in requests...
[GitHub] [OptIn Stars] GraphQL Query Failed: GraphQL Query Errors: Resource not accessible by integration
Error: Process completed with exit code 1.
It is not a regression, and the error message is lying about what it is. Evidence below.
Proof that this is transient, not a permissions misconfiguration
Same commit 635164bd, same token config, same query — four hours apart:
09:13 → run 29569220823, Run DevIndex Opt-In = success
09:14:29.360 [OptIn] Checking for new opt-in requests...
09:14:29.830 [OptIn] Checking for new opt-in issue requests... ← the stargazers query PASSED
09:14:29.975 [OptIn] No new opt-in requests found.
13:51 → run 29585619400, same step = failure
13:52:32.400 [OptIn] Checking for new opt-in requests...
13:52:32.708 [GitHub] [OptIn Stars] GraphQL Query Failed: ... Resource not accessible by integration
If GITHUB_TOKEN structurally could not read neomjs/devindex-opt-in, 09:13 would have failed identically. It did not. So GitHub intermittently returns Resource not accessible by integration for a query it otherwise permits — a transient condition wearing a permissions error's costume. (neomjs/devindex-opt-in verified PUBLIC, not archived, 16 stargazers.)
Exonerated, so nobody re-audits them:
- #15336 / #15328 (Emmy) — landed 08:29; Opt-In succeeded at 09:13, after it. It also only touched the REST path; this fails on GraphQL.
- The
code scanning merge protection ruleset — created 07:49; Opt-In succeeded at 09:13, after it.
The actual bug: the GraphQL transport treats a transient error as fatal
apps/devindex/services/GitHub.mjs — query() is called as GitHub.query(query, variables, 3, 'OptIn Stars'), i.e. with 3 retries available. It never used one. The log shows GraphQL Query Failed exactly once.
The classification has no branch for this error class:
- fatal, no retry:
Could not resolve to a User, NOT_FOUND, GraphQL Fatal Error
- retry: body-gateway errors (
502/504), or messages containing fetch / network / terminated
- everything else → straight to
throw new Error('GraphQL Query Errors: ...')
Resource not accessible by integration matches none of those tokens, so it throws on attempt 1. OptIn.run (OptIn.mjs:76) only swallows NOT_FOUND / Could not resolve, rethrows the rest — and the step exits 1, failing the whole pipeline.
This is the GraphQL half of #15328. That ticket gave the REST path a real transient story: restRetryableHttpStatuses: [429, 502, 503, 504], exponential backoff, jitter, retry-after honouring. The GraphQL path still has the old narrow substring matching. Same failure class, one transport got the fix. #15328 is closed and its owner's family is currently dark, so this is a fresh ticket rather than a reopen.
Wider signal — this pipeline is broadly unhealthy, and that is the bigger story
The Opt-In failure is not one bad step. Recent Data Sync Pipeline runs, failing at four different steps:
| run |
when |
failed step |
| 29585619400 |
07-17 13:51 |
Run DevIndex Opt-In |
| 29576742831 |
07-17 11:24 |
Run DevIndex Opt-In |
| 29569220823 |
07-17 09:13 |
Rebuild Indexes & SEO |
| 29559832086 |
07-17 06:22 |
Commit, Rebase and Push |
| 29541194846 |
07-16 23:02 |
Run DevIndex Spider |
| 29527676442 |
07-16 19:21 |
Rebuild Indexes & SEO |
| 29479895299 |
07-16 07:25 |
Commit, Rebase and Push |
~50% of runs since 07-16 have failed, each at a different step. That is not one regression; it is a pipeline with no transient story on any of its GitHub-touching steps. And it is all-or-nothing: at 09:13, Opt-In/Opt-Out/Spider all succeeded, Rebuild Indexes & SEO failed, and Commit, Rebase and Push was skipped — so hours of successful work synced nothing. This matters more than it looks: per AGENTS.md §critical_gates, the data-sync pipeline is the one sanctioned exception to "no direct push to dev". When it silently fails half the time, portal data quietly drifts from truth and nobody is paged.
Scope
In: give the GraphQL path the same transient story #15328 gave REST — a shared, explicit retryable-classification (bounded, backoff+jitter), with Resource not accessible by integration in it. A bounded retry is correct for both readings: transient → self-heals; a genuine permission misconfig → still fails loudly, ~6s later, with the same message.
Out (surface, don't bundle): the pipeline's all-or-nothing shape and the other three failing steps. Worth its own ticket once this lands and we can see what remains — I am not inferring their root causes from this one.
Acceptance Criteria
Avoided Traps
- Do not "fix" this by widening the workflow's
permissions: block. The 09:13 success proves the current scope is sufficient. Widening a token's scope to paper over a transient error grants standing privilege for a problem that is not about privilege.
- Do not substring-match
'Resource not accessible' and call it done. That is the pattern that produced this gap: a hand-maintained token list on GraphQL that #15328 already replaced with real classification on REST. Unify, don't extend the thing that failed.
- Do not assume the other three failing steps share this root cause. Different steps, unverified; naming them here is a signal, not a diagnosis.
Retrieval Hint: "devindex data sync pipeline Resource not accessible by integration GraphQL retry transient OptIn stargazers GitHub.mjs query classification 15328 REST half"
Premise
@tobiu reported run 29585619400 as a new regression:
It is not a regression, and the error message is lying about what it is. Evidence below.
Proof that this is transient, not a permissions misconfiguration
Same commit
635164bd, same token config, same query — four hours apart:09:13 → run 29569220823,
Run DevIndex Opt-In= success13:51 → run 29585619400, same step = failure
If
GITHUB_TOKENstructurally could not readneomjs/devindex-opt-in, 09:13 would have failed identically. It did not. So GitHub intermittently returnsResource not accessible by integrationfor a query it otherwise permits — a transient condition wearing a permissions error's costume. (neomjs/devindex-opt-inverified PUBLIC, not archived, 16 stargazers.)Exonerated, so nobody re-audits them:
code scanning merge protectionruleset — created 07:49; Opt-In succeeded at 09:13, after it.The actual bug: the GraphQL transport treats a transient error as fatal
apps/devindex/services/GitHub.mjs—query()is called asGitHub.query(query, variables, 3, 'OptIn Stars'), i.e. with 3 retries available. It never used one. The log showsGraphQL Query Failedexactly once.The classification has no branch for this error class:
Could not resolve to a User,NOT_FOUND,GraphQL Fatal Error502/504), or messages containingfetch/network/terminatedthrow new Error('GraphQL Query Errors: ...')Resource not accessible by integrationmatches none of those tokens, so it throws on attempt 1.OptIn.run(OptIn.mjs:76) only swallowsNOT_FOUND/Could not resolve, rethrows the rest — and the step exits 1, failing the whole pipeline.This is the GraphQL half of #15328. That ticket gave the REST path a real transient story:
restRetryableHttpStatuses: [429, 502, 503, 504], exponential backoff, jitter,retry-afterhonouring. The GraphQL path still has the old narrow substring matching. Same failure class, one transport got the fix. #15328 is closed and its owner's family is currently dark, so this is a fresh ticket rather than a reopen.Wider signal — this pipeline is broadly unhealthy, and that is the bigger story
The Opt-In failure is not one bad step. Recent
Data Sync Pipelineruns, failing at four different steps:~50% of runs since 07-16 have failed, each at a different step. That is not one regression; it is a pipeline with no transient story on any of its GitHub-touching steps. And it is all-or-nothing: at 09:13, Opt-In/Opt-Out/Spider all succeeded,
Rebuild Indexes & SEOfailed, andCommit, Rebase and Pushwas skipped — so hours of successful work synced nothing. This matters more than it looks: perAGENTS.md§critical_gates, the data-sync pipeline is the one sanctioned exception to "no direct push todev". When it silently fails half the time, portal data quietly drifts from truth and nobody is paged.Scope
In: give the GraphQL path the same transient story #15328 gave REST — a shared, explicit retryable-classification (bounded, backoff+jitter), with
Resource not accessible by integrationin it. A bounded retry is correct for both readings: transient → self-heals; a genuine permission misconfig → still fails loudly, ~6s later, with the same message.Out (surface, don't bundle): the pipeline's all-or-nothing shape and the other three failing steps. Worth its own ticket once this lands and we can see what remains — I am not inferring their root causes from this one.
Acceptance Criteria
Resource not accessible by integrationon GraphQL is retried (bounded) rather than fatal on attempt 1.NOT_FOUND,Could not resolve to a User) still fails fast — no retry storm on real misconfiguration.OptInalready passes in are actually reachable for this class.Avoided Traps
permissions:block. The 09:13 success proves the current scope is sufficient. Widening a token's scope to paper over a transient error grants standing privilege for a problem that is not about privilege.'Resource not accessible'and call it done. That is the pattern that produced this gap: a hand-maintained token list on GraphQL that #15328 already replaced with real classification on REST. Unify, don't extend the thing that failed.Retrieval Hint: "devindex data sync pipeline Resource not accessible by integration GraphQL retry transient OptIn stargazers GitHub.mjs query classification 15328 REST half"