Troubleshooting X Search Failures in last30days-skill: Root Causes and Fixes

X searches in last30days-skill fail most often due to missing Node.js dependencies, invalid authentication cookies, or overly generic queries that exhaust the three-tier retry fallback system implemented in bird_x.py.

The last30days-skill repository relies on a sophisticated Python-to-Node.js bridge to query X’s GraphQL API. When troubleshooting X search failures in last30days-skill, understanding the interaction between the bird_x.py wrapper and the vendored Bird module is essential for resolving credential, timeout, and query parsing issues quickly.

How the X Search Architecture Works

The bird_x.py Wrapper Layer

According to the mvanhorn/last30days-skill source code, the X search capability is implemented in scripts/lib/bird_x.py. This file serves as a thin Python wrapper around a vendored Bird Node.js module located at scripts/lib/vendor/bird-search/bird-search.mjs. The wrapper handles subprocess management, credential injection, and robust retry logic while the underlying Node.js component communicates directly with X’s GraphQL API.

The wrapper exposes several key functions:

  • set_credentials() (lines 33‑50) – Injects AUTH_TOKEN and CT0 cookies from a .env file into the subprocess environment
  • is_bird_installed() (lines 74‑83) – Verifies that bird-search.mjs exists and that Node.js is available on $PATH
  • is_bird_authenticated() (lines 85‑100) – Checks for injected credentials or tests browser cookie accessibility via node … --whoami
  • _run_bird_search() (lines 151‑165) – Executes the Node process with timeout enforcement and JSON output capture
  • search_x() (lines 48‑84) – Orchestrates the three-tier retry strategy and returns results or error dictionaries

Authentication and Environment Configuration

The _subprocess_env() helper merges optional credentials into the environment before spawning the Node process. You can inject credentials manually via set_credentials() or rely on the automatic loading in scripts/lib/env.py, which reads from your .env file during startup.

Core Subject Extraction and Retry Strategy

When search_x() receives a verbose natural language query like "best AI tools for coding", it delegates to _extract_core_subject() (lines 63‑71), which calls query.extract_core_subject() to generate a concise literal keyword list that X can match.

If the initial literal search returns zero items, search_x() automatically executes three progressive fallbacks:

  1. OR-group expansion – Searches compound terms as "Claude plugin" OR "Code plugin"
  2. Shortened two-word query – Reduces to the most significant two-word phrase
  3. Strongest token fallback – Searches only the strongest non-stopword single token

Common X Search Failure Modes and Solutions

Empty Result Sets After Progressive Retries

Symptom: The response contains "items": [] even after all three retry attempts.

Root cause: The core subject extracted from your query is too generic (e.g., "trendiest") or X’s index contains no matching tweets within the requested date window.

Fix: Rephrase your query with specific technical terms or widen the from_date and to_date parameters. Check the verbose logs to see which fallback branch was exhausted.

Timeout and Process Management Errors

Symptom: Response contains {"error":"Search timed out …"}.

Root cause: Network slowness, X API throttling, or a deadlocked Node process.

Technical details: The _run_bird_search() function enforces strict timeouts based on search depth: 30 seconds for "quick", 45 seconds for "default", and 60 seconds for "deep". When timeouts trigger, the function kills the entire process group to prevent zombie processes.

Installation and Environment Errors

Symptom: Response contains {"error":"Bird search failed"} or is_bird_installed() returns False.

Root cause: The node binary is missing from $PATH, or scripts/lib/vendor/bird-search/bird-search.mjs does not exist.

Fix: Verify Node.js version 22+ is installed and accessible. Confirm the vendored Bird module exists at the expected path:

ls scripts/lib/vendor/bird-search/bird-search.mjs
node --version

Invalid JSON and Authentication Failures

Symptom: Response contains {"error":"Invalid JSON response"} or authentication warnings.

Root cause: The Bird module crashed and printed a stack trace instead of valid JSON, or AUTH_TOKEN/CT0 cookies are missing/invalid.

Technical details: _run_bird_search() catches json.JSONDecodeError when the subprocess outputs non-JSON data. For authentication issues, is_bird_authenticated() returns None when neither environment credentials nor browser cookies are available, causing the research engine in scripts/last30days.py (lines 346‑356) to skip X searches entirely.

Practical Debugging Techniques

Enabling Verbose Logging

All internal events are written to stderr via the internal _log() function. Capture diagnostic output by redirecting stderr when invoking the skill:

python3 -m scripts.last30days "Claude plugins" 2>bird_debug.log

Typical log output showing the retry progression:


[Bird] Searching: Claude plugins since:2026-02-01
[Bird] 0 results for 'Claude plugins', retrying with OR groups: "Claude plugin" OR "Code plugin"
[Bird] 0 results for 'Claude plugins', retrying with 'Claude plugins'
[Bird] 0 results for 'Claude plugins', retrying with strongest token 'Claude'

Checking Installation and Authentication Status

Run this diagnostic script to verify your environment:

from lib import bird_x

print("Installed?", bird_x.is_bird_installed())
print("Authenticated?", bird_x.is_bird_authenticated())

Expected healthy output:


Installed? True
Authenticated? env AUTH_TOKEN

If Installed? is False, reinstall dependencies. If Authenticated? is None, check your .env file for AUTH_TOKEN and CT0 values or ensure the Bird module can access browser cookies.

Performing a Manual Search Test

Isolate X search issues by calling the wrapper directly:

from lib import bird_x

# Optional: Explicitly set credentials

bird_x.set_credentials(
    auth_token="YOUR_AUTH_TOKEN",
    ct0="YOUR_CT0_COOKIE"
)

# Execute search for last 30 days

result = bird_x.search_x(
    topic="Rust web frameworks",
    from_date="2026-01-01",
    to_date="2026-01-30",
    depth="default"
)

if result.get("error"):
    print(f"Error type: {result['error']}")
else:
    print(f"Retrieved {len(result.get('items', []))} tweets")

Summary

  • X search failures in last30days-skill originate in scripts/lib/bird_x.py, which wraps a Node.js subprocess communicating with X’s GraphQL API.
  • Authentication requires valid AUTH_TOKEN and CT0 cookies injected via set_credentials() or accessible browser cookies.
  • Timeouts are enforced at 30s/45s/60s depending on search depth; persistent hangs indicate network issues or X throttling.
  • Empty results trigger three automatic retries (OR-groups, shortened phrases, strongest token), but overly generic queries may still return nothing.
  • Installation errors occur when Node.js is missing or bird-search.mjs is not present in scripts/lib/vendor/bird-search/.

Frequently Asked Questions

Why does my X search return zero results even after retries?

If the progressive fallback strategy in search_x() (lines 48‑84) exhausts all three attempts, the query is likely too generic or the date window is too narrow. The code first tries the literal query, then OR-grouped compound terms, then a shortened two-word version, and finally the strongest single token. If X’s index contains no matches for any variant, the skill returns an empty "items" list. Try rephrasing with specific technical keywords or widening the from_date and to_date parameters.

How do I verify that Bird is properly installed and accessible?

Call bird_x.is_bird_installed() to check for the presence of scripts/lib/vendor/bird-search/bird-search.mjs and a Node.js binary on $PATH. This function (lines 74‑83) returns True only if both the vendored module and the Node runtime are detected. If it returns False, verify your Node installation is version 22 or higher and that the vendor directory exists.

What causes "Search timed out" errors and how long should I wait?

The _run_bird_search() function (lines 151‑165) enforces timeouts of 30 seconds for "quick" searches, 45 seconds for "default", and 60 seconds for "deep" searches. These timeouts protect against network slowness or X API throttling. If you encounter this error consistently, the Node subprocess may be hanging on authentication or rate limiting. Check that your AUTH_TOKEN is valid and consider reducing search depth to "quick".

Can I use browser cookies instead of manually setting AUTH_TOKEN and CT0?

Yes. The is_bird_authenticated() function (lines 85‑100) first checks for explicitly injected credentials, then attempts to run node bird-search.mjs --whoami to verify if the vendored Bird module can read cookies from your default browser. If this succeeds, the skill will use those cookies automatically. If both methods fail, the caller in scripts/last30days.py will skip X searches or emit a warning, depending on the configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →