{"record":{"id":"6284596085325094","repo":"mvanhorn/last30days-skill","slug":"all-x-backends-failed-last-error","errorCode":null,"errorMessage":"All X backends failed — {last_error}","messagePattern":"All X backends failed — (.+?)","errorType":"exception","errorClass":"SourceRunError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/pipeline.py","lineNumber":4265,"sourceCode":"                    state = http.classify_failure(message=err)\n                    return items, _outcome_artifact(\n                        state,\n                        f\"X returned {len(items)} items but also errored: {err}\",\n                    )\n                # No auth issues and no prior errors - proceed to judge-retry\n                used_backend = backend\n                break\n            if err:\n                last_error = f\"{backend}: {err}\"\n                print(f\"[X] backend '{backend}' failed ({err}); trying next\", file=sys.stderr)\n\n        if not items and last_error:\n            state = (\n                bird_x.classify_run_failure(last_error)\n                if last_error.startswith(\"bird:\")\n                else http.classify_failure(message=last_error)\n            )\n            raise SourceRunError(f\"All X backends failed — {last_error}\", state)\n\n        # Retrieve-judge-retry: judge corpus and retry if off-topic flood.\n        # Skip retry on quick/mock (same as Phase 2).\n        artifact = {}\n        if items and depth != \"quick\" and not mock:\n            items_for_judge = [\n                {\"author_handle\": it.get(\"author_handle\", \"\"), \"text\": it.get(\"text\", \"\")}\n                for it in items\n            ]\n            if x_judge.should_retry_x_search(items_for_judge, x_query, ranking_query=ranking_query, depth=depth):\n                # Retry with cleaned query (1 retry, ≤2 extra grok calls)\n                # Strip noise words but preserve all significant terms to avoid\n                # losing disambiguating terms (e.g., \"react server components\")\n                core_tokens = query.extract_core_subject(x_query)\n                retry_query = core_tokens or x_query\n                print(f\"[X] corpus off-topic; retrying with '{retry_query}'\", file=sys.stderr)\n\n                if used_backend:","sourceCodeStart":4247,"sourceCodeEnd":4283,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/pipeline.py#L4247-L4283","documentation":"SourceRunError raised after the X backend chain is exhausted: every backend (bird, ScrapeCreators, grok, ...) either returned no items or errored, and at least one recorded an error (last_error). The failure state is classified — bird_x.classify_run_failure for 'bird:'-prefixed errors, otherwise http.classify_failure — so callers can distinguish auth, rate-limit, and network classes.","triggerScenarios":"Iterating the chain via _fetch_x_backend: each backend returns (items, err); a backend with items breaks the loop. If no backend yields items and last_error is non-empty (e.g. 'bird: token revoked' then 'scrapecreators: 429'), the SourceRunError is raised with the last error text.","commonSituations":"Revoked grok OAuth token plus exhausted ScrapeCreators quota; sustained rate limiting across all backends during heavy usage; upstream API outage; malformed query strings rejected by every backend.","solutions":["Read the error state: AUTH_FAILED → re-auth the failing backend (device flow / new key); RATE_LIMITED → back off and retry later.","Verify the ScrapeCreators key with a minimal API call (check status code, never print the key).","Pin a known-good backend at runtime (runtime.x_search_backend) to skip a broken primary.","Retry the run after transient outages; if persistent, drop 'x' from sources to still get results elsewhere."],"exampleFix":"# before\nitems, meta = fetch_x(subquery, ...)\n\n# after\ntry:\n    items, meta = fetch_x(subquery, ...)\nexcept SourceRunError as exc:\n    if \"AUTH\" in str(exc.state):\n        refresh_x_credentials()\n        items, meta = fetch_x(subquery, ...)\n    else:\n        raise","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    items, meta = fetch_x(...)\nexcept SourceRunError as exc:\n    state = str(exc.state)\n    if \"RATE\" in state:\n        time.sleep(60)\n        items, meta = fetch_x(...)\n    elif \"AUTH\" in state:\n        refresh_x_credentials()\n        raise  # caller must re-run with fixed credentials\n    else:\n        raise","preventionTips":["Keep more than one X backend configured (bird session + ScrapeCreators) so the chain has failover depth.","Parse the trailing '<backend>: <err>' text to identify which backend died first.","Rotate or refresh OAuth-backed backends (grok) on AUTH-classified failures instead of retrying."],"tags":["x","network","rate-limit","api-key","source-failure"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}