Skip to content

Troubleshooting

Garot Conklin edited this page May 14, 2026 · 1 revision

Troubleshooting


macOS kernel panic during scheduled run

Symptom: The Mac locks up completely during the 5am launchd run. Forced reboot required. logs/cron.log shows the pipeline start and collection phase, then cuts off abruptly during Pass 1 — no error message, no Python traceback.

Root cause: Ollama uses Apple Metal for GPU inference. When invoked from a launchd background daemon, Metal access runs in a non-interactive GPU session context that differs from a normal user login session. This mismatch triggers a Metal/GPU driver kernel panic.

Resolution: Switch to Claude Code CLI as the LLM provider for all automated runs. Claude makes HTTPS calls to Anthropic's API — no Metal, no GPU, no kernel-level resources.

The current configuration already has this fix applied:

  • config/sources.yaml: llm.provider: claude
  • scripts/com.flexrpl.signal.plist: SIGNAL_LLM_PROVIDER=claude
  • scripts/run_and_publish.sh: export SIGNAL_LLM_PROVIDER="${SIGNAL_LLM_PROVIDER:-claude}"

Important: Do not change SIGNAL_LLM_PROVIDER to ollama in the plist or script. Use Ollama only for interactive manual runs from the terminal.

For the full investigation (system logs, panic file analysis, reboot confirmation via last reboot), see docs/troubleshooting-macos-crash.md in the repository.


launchd job not running

Check it's loaded:

launchctl list | grep flexrpl

If missing:

launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.plist

Check the log:

cat logs/cron.log

Schedule didn't update after editing the plist:

The active copy in ~/Library/LaunchAgents/ was not updated. Always run:

launchctl unload ~/Library/LaunchAgents/com.flexrpl.signal.plist
cp scripts/com.flexrpl.signal.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.plist

Claude CLI errors

Cannot reach Claude CLI:

which claude          # should return /opt/homebrew/bin/claude
claude --version      # should return 2.x.x (Claude Code)

If missing:

npm install -g @anthropic-ai/claude-code
claude auth login

Claude CLI non-zero exit during Pass 1:

Claude rate-limited or returned an error. The pipeline logs the error and continues with the next article — it doesn't abort. Check logs/cron.log for the frequency of these errors.


Ollama errors (manual runs only)

Cannot reach Ollama:

ollama serve   # start the server
ollama list    # confirm qwen2.5:14b is present

Model not found:

ollama pull qwen2.5:14b

No articles collected

No articles collected. Check feeds / network.

Run the smoke test to identify which feeds are failing:

python3 main.py --collect-only

Look for ✗ <source name>: <error> lines in the output. Common causes:

  • Source changed its RSS URL
  • Network connectivity issue
  • Source's RSS feed is temporarily down

JSON parse failures in Pass 3 or 4

LLM error: ...

The LLM returned malformed JSON. _parse_json_response() tries several recovery strategies (strip markdown fences, regex search for JSON object) before returning None. When it returns None, the pass uses a safe fallback dict and continues.

This is non-deterministic — re-running the pipeline usually resolves it. If it happens consistently for the same cluster, the article content may be triggering unusual LLM output (e.g. articles with non-English characters, very short articles, or articles with unusual formatting).


Pass 5 hangs or times out

Pass 5 uses a min(timeout * 3, 360) timeout (max 6 minutes). If it consistently times out:

  1. Increase the timeout in config/sources.yaml:

    claude:
      timeout: 240 # 4 minutes → Pass 5 gets up to 360s (capped)
  2. Check if the prompt is hitting context window limits. The story_analyses input is capped at 6000 characters in synthesize_brief().


FOREIGN KEY constraint failed in store.py

This means a run_id or cluster_id being written doesn't match an existing record in the parent table.

Most likely cause: signal.db was corrupted (e.g. by a mid-run crash).

Fix:

rm signal.db
python3 main.py

The database is recreated automatically. All previous analysis is lost but the HTML reports in reports/ remain.


ModuleNotFoundError when running with --no-venv

--no-venv skips venv setup and runs with the current Python interpreter directly. If that interpreter doesn't have the dependencies installed, you'll get ModuleNotFoundError.

The run_and_publish.sh script uses the venv Python explicitly:

PYTHON="$REPO/.venv/bin/python"
"$PYTHON" main.py --no-venv

If you deleted .venv, run without --no-venv once to recreate it:

python3 main.py

Archive shows stale data (yesterday's report as "latest")

The archive was not updated in the most recent commit. This happens if archive.html was not included in the git add.

Fix immediately:

git add archive.html
git commit -m "fix: update archive.html"
git push origin main

The run_and_publish.sh script now explicitly stages archive.html on every run to prevent this.


Report Home button goes to 404

The "◁ Home" button uses a relative path: href="../index.html". This works correctly when reports are served from reports/brief_*.html on GitHub Pages (parent directory is the repo root where index.html lives).

If viewing a report by opening the HTML file directly from the filesystem (file:///.../reports/brief_*.html), the relative path may not resolve correctly depending on your browser. Open it through a local server or directly via open from the terminal.


Diagnostic commands reference

# Check for unexpected reboots
last reboot

# List system crash and panic logs
ls -lt /Library/Logs/DiagnosticReports/

# Check launchd events for the signal job
log show --predicate 'eventMessage CONTAINS "flexrpl"' --style compact

# Check system logs around a specific window
log show --start "YYYY-MM-DD HH:MM:00" --end "YYYY-MM-DD HH:MM:00" \
  --predicate 'processImagePath contains "python" OR eventMessage contains "panic"' \
  --style compact

# Check what caused a bash crash report
grep -o '"responsibleProc"[^,]*\|"coalitionName"[^,]*' \
  ~/Library/Logs/DiagnosticReports/bash-<timestamp>.ips

# Confirm Python process is running during a slow pass
ps aux | grep python | grep -v grep

# Confirm Claude subprocesses are spawning during Pass 1
ps aux | grep claude | grep -v grep

Clone this wiki locally