# Troubleshooting

> --diagnose probes, missing-source recovery, stale Claude marketplace clones, thin results, and documented failure modes.

- Repository: mvanhorn/last30days-skill
- GitHub: https://github.com/mvanhorn/last30days-skill
- Human docs: https://grok-wiki.com/public/docs/mvanhorn-last30days-skill-50b5421a8cca
- Complete Markdown: https://grok-wiki.com/public/docs/mvanhorn-last30days-skill-50b5421a8cca/llms-full.txt

## Source Files

- `CONFIGURATION.md`
- `skills/last30days/scripts/last30days.py`
- `skills/last30days/SKILL.md`
- `skills/last30days/scripts/lib/setup_wizard.py`
- `docs/solutions/workflow-issues/release-consistency-test-cascade-2026-05-16.md`
- `docs/solutions/architecture/search-quality-eval-manual-by-default-2026-05-10.md`

---

---
title: "Troubleshooting"
description: "--diagnose probes, missing-source recovery, stale Claude marketplace clones, thin results, and documented failure modes."
---

The engine exposes a non-destructive availability probe via `--diagnose`: `last30days.py` loads config from `.claude/last30days.env` or `~/.config/last30days/.env`, calls `pipeline.diagnose()`, prints JSON, and exits before any retrieval. Full runs add post-research quality nudges, pipeline thin-source retries, and structured warnings in the report footer.

```mermaid
flowchart TD
  subgraph probe [Probe path]
    CLI["last30days.py --diagnose"]
    ENV["env.get_config()"]
    DIAG["pipeline.diagnose()"]
    JSON["stdout: JSON + exit 0"]
    CLI --> ENV --> DIAG --> JSON
  end
  subgraph run [Research path]
    RUN["last30days.py TOPIC"]
    PF["preflight.check_class_1_trap()"]
    PIPE["pipeline.run()"]
    RETRY["_retry_thin_sources()"]
    NUDGE["quality_nudge → stderr"]
    RUN --> PF
    PF -->|pass| PIPE
    PIPE --> RETRY
    PIPE --> NUDGE
  end
```

## Run `--diagnose` first

Use the direct CLI form from the skill directory (not a slash command with pipes):

<CodeGroup>
```bash title="From repo checkout"
cd skills/last30days
python3 scripts/last30days.py --diagnose
```

```bash title="Scoped sources"
python3 scripts/last30days.py --diagnose --search reddit,x,youtube
```
</CodeGroup>

<Check>
Exit code `0` and JSON on stdout means config loaded and probes completed. No topic argument is required.
</Check>

Hermes and other hosts that pin Python 3.12+ should use the same entrypoint documented in `HERMES_SETUP.md`: `python3.12 scripts/last30days.py --diagnose`.

Maintainers can fold diagnose into release verification via `scripts/verify_v3.py`, which parses `--diagnose` stdout as a smoke gate.

## Diagnose JSON fields

`pipeline.diagnose(config, requested_sources)` returns a single object. Use it to separate “key missing” from “source errored mid-run.”

| Field | Meaning |
| --- | --- |
| `providers` | `google`, `openai`, `xai`, `openrouter` booleans (OpenAI requires `OPENAI_AUTH_STATUS == ok`) |
| `local_mode` | `true` when no cloud reasoning keys are configured |
| `reasoning_provider` | `LAST30DAYS_REASONING_PROVIDER` or `"auto"` |
| `available_sources` | Sources `pipeline.run()` would fan out to (after `EXCLUDE_SOURCES`, `INCLUDE_SOURCES`, CLI `--search`) |
| `x_backend` | Resolved X path: Bird cookies, xAI, ScrapeCreators, etc. |
| `bird_installed` / `bird_authenticated` / `bird_username` | Bundled Bird CLI state |
| `native_web_backend` | First configured web key: `brave`, `exa`, `serper`, or `parallel` |
| `has_scrapecreators` | `SCRAPECREATORS_API_KEY` present |
| `has_github` | `GITHUB_TOKEN` or `gh` on `PATH` |

<Warning>
`available_sources` empty for a normal topic (no `--search` narrowing) leads to `RuntimeError: No sources are available for this run.` Fix credentials before researching.
</Warning>

## Missing-source recovery

Default availability is computed in `pipeline.available_sources()` from env keys and CLI presence. The table maps common gaps to fixes.

| Symptom in diagnose / footer | Typical cause | Recovery |
| --- | --- | --- |
| `x` not in `available_sources` | No X auth | Log into x.com in a supported browser and re-run, or set `AUTH_TOKEN`+`CT0`, `XAI_API_KEY`, `SCRAPECREATORS_API_KEY`, or `FROM_BROWSER=firefox` |
| `youtube` missing | `yt-dlp` not on `PATH` | `brew install yt-dlp` (setup wizard can auto-install via Homebrew) |
| `grounding` missing | No web API key | Add `BRAVE_API_KEY`, `EXA_API_KEY`, `SERPER_API_KEY`, or `PARALLEL_API_KEY`; on Claude Code/Codex/Gemini the host WebSearch may suffice without keys |
| `tiktok` / `instagram` missing | No SC key or not in `INCLUDE_SOURCES` | Set `SCRAPECREATORS_API_KEY` and `INCLUDE_SOURCES=tiktok,instagram` |
| `bluesky` missing | Credentials absent | `BSKY_HANDLE` + `BSKY_APP_PASSWORD` (19-char app password); default search host is `api.bsky.app` — override with `BSKY_SEARCH_HOST` if infrastructure moves |
| Reddit “public only” | No `SCRAPECREATORS_API_KEY` | Reddit still works keyless; SC unlocks full threads with comments |
| `.env` ignored | Wrong path | Project `.claude/last30days.env` wins over `~/.config/last30days/.env`; set `LAST30DAYS_CONFIG_DIR` to relocate |

<Steps>
<Step title="Run first-time setup">
```bash
python3 scripts/last30days.py setup
```
Extracts browser cookies for configured domains, checks or installs `yt-dlp`, writes `SETUP_COMPLETE=true` and `FROM_BROWSER` to the active config file.
</Step>
<Step title="Re-probe">
```bash
python3 scripts/last30days.py --diagnose
```
Confirm `available_sources` includes the platforms you expect.
</Step>
<Step title="Run a smoke topic">
```bash
python3 scripts/last30days.py "your topic" --emit=compact --quick
```
</Step>
</Steps>

Optional setup subcommands: `setup --openclaw`, `setup --github`, `setup --device-auth` (JSON on stdout).

<Tip>
On POSIX hosts, secrets files should be mode `600`. The engine warns every run if group/other can read `.env` — run `chmod 600` on the path shown in the warning.
</Tip>

After a full run, missing or degraded core sources also surface through `quality_nudge.compute_quality_score()` on stderr (advisory, not a hard failure).

## Stale Claude marketplace clone

Claude Code can load `SKILL.md` from `~/.claude/plugins/marketplaces/last30days-skill/`, a git clone that auto-restores to `origin/main` and may lag the versioned plugin cache by one or more releases. Documented impact: `--help` and engine flags from the stale tree (e.g. missing `--competitors`) while the cache already had them.

**Model-side guard (STEP 0 in SKILL.md):** If the loaded path contains `/.claude/plugins/marketplaces/` and a newer cache exists under `~/.claude/plugins/cache/last30days-skill/last30days/{version}/`, re-read `SKILL.md` from the cache before any tool calls.

**Operator-side fixes:**

| Action | Command / note |
| --- | --- |
| Force plugin refresh | `claude plugin update last30days@last30days-skill` |
| Avoid duplicate installs | Use marketplace **or** `npx skills add … -a claude-code`, not both — duplicate `/last30days` entries mean two skill copies |
| Prefer versioned cache | Let marketplace update populate `~/.claude/plugins/cache/…` before relying on marketplaces path |

<Info>
`~/.codex/skills/`, `~/.agents/skills/`, `npx skills` global installs, and repo checkouts are valid; STEP 0 does not redirect those paths. Engine invocation uses `SKILL_DIR` from the `SKILL.md` the harness actually loaded.
</Info>

## Stale Agent Skills / dev-tree install

For Codex, Cursor, Gemini, and other Agent Skills hosts, `npx skills add` copies into `~/.agents/skills/<name>/` **frozen at install time**. Working-tree edits do not propagate until you re-sync:

```bash
npx skills add mvanhorn/last30days-skill -g -y
# or from a local repo:
npx skills add . -g -y
```

Live development symlink (repo root):

```bash
ln -sfn "$PWD/skills/last30days" ~/.agents/skills/last30days
```

Update published installs: `npx skills update last30days -g`.

## Thin results and low evidence

Thin output can come from query traps, sparse platforms, missing web backend, or per-source retrieval limits — not only “broken install.”

### Engine behaviors

| Mechanism | When it runs | Effect |
| --- | --- | --- |
| `_retry_thin_sources()` | Default/deep depth only; skipped in `--quick` | Re-queries sources with fewer than 3 items using `query.extract_core_subject(topic, max_words=3)`; skips sources in `errors_by_source` and rate-limited sources |
| `_warnings()` | End of `pipeline.run()` | Adds `"Evidence is thin for this topic."` when fewer than 5 ranked candidates; lists `errors_by_source` |
| Cluster `uncertainty` | `cluster._cluster_uncertainty()` | `thin-evidence` when max cluster score &lt; 55; `single-source` when one platform dominates |
| `--quick` | User flag | Disables thin-source retry — faster but sparser |

### Operator levers

- Add or fix web search keys (`BRAVE_API_KEY`, etc.) — CONFIGURATION.md notes client setups often look thinner than a host with native WebSearch.
- Use `--deep` for higher-recall depth settings (more pool/rerank budget).
- Narrow with `--subreddits`, `--x-handle`, `--github-repo` when the topic is broad.
- Avoid demographic-shopping phrasing without hobbies/relationship/budget (see preflight below).

### Post-run quality nudge (stderr)

After retrieval, `quality_nudge` scores five core sources (`hn`, `polymarket`, `x`, `youtube`, `reddit`) and may emit actionable text:

| Signal | Detection | Typical fix |
| --- | --- | --- |
| Missing X | No creds or `x_error` | Browser login or `XAI_API_KEY` |
| Missing YouTube | No `yt-dlp` | `brew install yt-dlp` |
| Degraded YouTube | Videos found, transcript ratio below `DEGRADED_TRANSCRIPT_THRESHOLD` (default 0.5) | `brew upgrade yt-dlp` / `pip install -U yt-dlp` — stale binary is the canonical failure mode |
| Silent Instagram | SC configured, zero items, not excluded | Topic may lack reel coverage; SC v2 endpoint flakiness; skill retries hashtag form automatically |

Captions-disabled YouTube videos are excluded from the degraded ratio so one uploader-disabled track does not false-trigger a stale-`yt-dlp` nudge.

## Preflight refuse gates (exit code 2)

Before the pipeline starts, `preflight.check_class_1_trap(topic)` blocks Class 1 demographic-shopping patterns unless qualifiers (budget, hobbies, relationship, activity noun) are present. Stderr prints a structured `REFUSE:` message; the model should ask for context and re-run with an enriched query.

Bypass only when the user insists:

```bash
LAST30DAYS_SKIP_PREFLIGHT=1 python3 scripts/last30days.py "birthday gift for 40 year old man"
```

SKILL.md Step 0.45 documents additional trap classes (numeric keyword collision, tutorial phrasing, generic single nouns) handled at the harness layer before engine invocation.

## Documented failure modes

| Failure mode | Surface | Mitigation |
| --- | --- | --- |
| Stale marketplace SKILL.md | Missing flags, wrong Step 0 behavior | STEP 0 cache redirect; `claude plugin update` |
| v3.0.6 “generic research” regression | Invented titles, `Sources:` footer, skipped Step 0.55 | Mandatory badge, LAWs block at top of SKILL.md, `SKILL_DIR`-scoped engine |
| Class 1 keyword trap | Irrelevant Reddit/HN noise | Engine refuse + Step 0.45 reframe/`--subreddits` |
| Stale `yt-dlp` | Videos without transcripts | Quality nudge + upgrade binary |
| Instagram SC silent zero | No section, no error | Documented in `quality_nudge`; check `INCLUDE_SOURCES` / topic shape |
| No sources available | Immediate `RuntimeError` | `--diagnose`; add keys per table above |
| Open PR CI cascade on version pins | Unrelated PRs red after release | Resolved by removing `sync.sh` pin test (see `docs/solutions/workflow-issues/release-consistency-test-cascade-2026-05-16.md`) |
| Search-quality regressions | Not caught in default CI | `evaluate_search_quality.py` is manual / `workflow_dispatch` by design (`docs/solutions/architecture/search-quality-eval-manual-by-default-2026-05-10.md`) |

<Note>
Search-quality eval intentionally stays out of default PR CI: live APIs, cost, and LLM-judge non-determinism. Request manual eval on retrieval/ranking changes; do not expect pytest alone to catch relevance regressions.
</Note>

## Runtime UI and errors in output

During a run, `ProgressDisplay` shows per-source counts at completion. If Reddit and web grounding are both unavailable, `show_promo()` nudges configuration (suppressed when `--plan` or `--competitors-plan` indicates a hosting model with native WebSearch).

Rendered `--emit=md` / compact output includes **Source Coverage** and **Source Errors** sections when `report.errors_by_source` is non-empty. Warnings from `_warnings()` pass through in the report payload for synthesis.

For SSH/datacenter YouTube blocks, set `LAST30DAYS_YOUTUBE_SSH_HOST` in config so `yt-dlp` routes through `ssh` (see `youtube_yt.py`).

## Debug and verification flags

<ParamField body="LAST30DAYS_DEBUG" type="string">
Set `1` in environment or config (or pass `--debug`) to enable HTTP debug logging.
</ParamField>

<ParamField body="LAST30DAYS_SKIP_PREFLIGHT" type="string">
Bypass Class 1 refuse gate when set before invocation.
</ParamField>

<ParamField body="--mock" type="flag">
Use fixture retrieval; useful for tests without live API keys.
</ParamField>

## Related pages

<CardGroup>
<Card title="Configure sources" href="/configure-sources">
Credential layers, `INCLUDE_SOURCES`, and per-source API keys tied to diagnose output.
</Card>
<Card title="Quickstart" href="/quickstart">
First invocation, setup wizard, and `--diagnose` verification in the happy path.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full flag list including `--diagnose`, `--search`, depth profiles, and direct CLI constraints.
</Card>
<Card title="Skill contract" href="/skill-contract">
STEP 0 stale-clone guard, pre-flight protocol, and `SKILL_DIR` engine substitution.
</Card>
<Card title="Model clients" href="/model-clients">
Per-harness install, marketplace vs `npx skills`, and stale-install avoidance.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
Env var catalog, `.env` lookup order, and web-backend priority.
</Card>
</CardGroup>
