{"record":{"id":"65922828d819459b","repo":"mvanhorn/last30days-skill","slug":"no-x-backend-is-available","errorCode":null,"errorMessage":"No X backend is available.","messagePattern":"No X backend is available\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/pipeline.py","lineNumber":4208,"sourceCode":"                f\"{public_failure}\",\n            )\n        return merged, {}\n    if source == \"x\":\n        # Compile X query from raw_topic (like Reddit/YouTube), not planner's\n        # search_query which may contain operator strings like \"Rome Italy\".\n        x_query = raw_topic or topic or subquery.search_query\n        ranking_query = subquery.ranking_query\n\n        # One X source, an ordered chain of interchangeable backends. Try the\n        # primary; fall through to the next only if it returns nothing or errors.\n        chain = env.x_backend_chain(config)\n        # Trust an explicit runtime backend as the primary (already resolved as\n        # available), keeping the rest of the chain as failover backups.\n        pinned = runtime.x_search_backend\n        if pinned:\n            chain = [pinned] + [b for b in chain if b != pinned]\n        if not chain:\n            raise RuntimeError(\"No X backend is available.\")\n        last_error = \"\"\n        items = []\n        used_backend = None\n        for i, backend in enumerate(chain):\n            items, err = _fetch_x_backend(backend, x_query, from_date, to_date, depth, config)\n            if items:\n                if i > 0:\n                    print(f\"[X] primary backend(s) returned nothing; used fallback '{backend}'\", file=sys.stderr)\n                # Check for auth errors before proceeding to judge-retry\n                if last_error:\n                    # Fallback succeeded after earlier backend failed. Classify\n                    # the original error: if it was AUTH_FAILED (grok revoked),\n                    # preserve that state so user gets re-login guidance.\n                    prior_state = http.classify_failure(message=last_error)\n                    if prior_state == schema.AUTH_FAILED:\n                        # Keep AUTH_FAILED visible so host shows re-login hint\n                        return items, _outcome_artifact(\n                            schema.AUTH_FAILED,","sourceCodeStart":4190,"sourceCodeEnd":4226,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/pipeline.py#L4190-L4226","documentation":"RuntimeError raised in the X fetch leg when env.x_backend_chain(config) resolves to an empty chain AND no runtime-pinned backend (runtime.x_search_backend) is set. The chain is built from configured backends (e.g. bird session, ScrapeCreators/grok), so an empty chain means the config has no usable X credential at all. It is raised before any fetch is attempted.","triggerScenarios":"Calling the pipeline with source 'x' while: no ScrapeCreators key, no bird session file, and X_SEARCH_BACKEND env unset/empty; or every backend in the chain has been disabled via config. pinned is falsy, so chain stays [].","commonSituations":"Fresh install that skipped onboarding (no GitHub device-auth, no SCRAPECREATORS_API_KEY); expired/removed bird session; env var typos like X_BACKEND instead of the expected names; CI environments with no credentials.","solutions":["Set SCRAPECREATORS_API_KEY (onboarding persists it automatically via setup_wizard.write_api_key).","Re-run the setup wizard / GitHub device-auth flow to restore the bird or grok backend.","Or exclude 'x' from requested_sources and rely on keyless sources.","Check env.x_backend_chain(config) returns non-empty before running if you orchestrate configs dynamically."],"exampleFix":"# before\nrun_pipeline(topic, requested_sources=[\"x\"], config={})\n\n# after\nrun_pipeline(topic, requested_sources=[\"x\"], config={\"SCRAPECREATORS_API_KEY\": os.environ[\"SCRAPECREATORS_API_KEY\"]})","handlingStrategy":"validation","validationCode":"from skills.last30days.scripts.lib import env\nif not env.x_backend_chain(config):\n    raise SystemExit(\"no X backend configured: set SCRAPECREATORS_API_KEY or run setup\")","typeGuard":null,"tryCatchPattern":"try:\n    items, meta = fetch_x(...)\nexcept RuntimeError as exc:\n    if \"No X backend\" in str(exc):\n        sources.remove(\"x\")\n        items, meta = None, None  # continue with remaining sources","preventionTips":["Complete onboarding once; setup_wizard persists SCRAPECREATORS_API_KEY with 0o600 automatically.","Assert env.x_backend_chain(config) is non-empty before requesting the x source."],"tags":["x","configuration","credentials","api-key"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}