Troubleshooting Guide
Setup Wizard Issues
Wizard shows “No TTY detected” when using curl pipe
Problem: Running curl ... | bash without a terminal redirects stdin, so the interactive wizard cannot receive keyboard input.
Solution:
API key validation fails but key is correct
Problem: The wizard rejects an API key during live validation even though the key works.
Solutions:
- Check network connectivity — the wizard makes outbound HTTPS requests to api.anthropic.com, api.openai.com, api.telegram.org
- Use
--skip-validation to bypass live checks: pnpm wizard --skip-validation
- Corporate proxies or firewalls may block API calls — configure
HTTPS_PROXY env var before running wizard
”I have Claude Pro/Max subscription - can I use it for EchOS?”
Problem: Confusion about Anthropic subscription plans vs API access.
Answer: ❌ No, subscription plans cannot be used for EchOS.
- Claude Pro/Max subscriptions (20−200/month) are for using Claude through Anthropic’s web, desktop, and mobile apps only
- They do NOT provide API access for programmatic integration
- EchOS requires a separate Anthropic API account with pay-as-you-go billing
- API costs are typically much lower than subscriptions for automated use cases (often ~$5/month for typical personal use)
What you need:
- Sign up for Anthropic API access at https://console.anthropic.com/
- Add credits or set up billing
- Generate an API key under Settings → API Keys
- Use that key in EchOS
.env file
See: the Anthropic pricing page for a comparison of subscription vs API plans.
Problem: Missing required env vars.
Required variables for non-interactive mode:
ALLOWED_USER_IDS
- At least one LLM key:
ANTHROPIC_API_KEY or LLM_API_KEY
.env file has wrong permissions
Problem: Other users on the system can read your API keys.
Fix:
The wizard sets 0600 automatically, but if you created .env manually, set it yourself.
”No .env file found. Run: pnpm wizard” on startup
Problem: pnpm start exits immediately with this message.
Solution: Run the setup wizard:
Or for CI environments:
Build and Installation Issues
Cannot find package ‘@echos/shared’ (or other @echos/* packages)
Problem: Workspace packages aren’t built or aren’t being resolved by tsx.
Symptoms:
Solutions:
-
Build all packages first:
-
If build fails, clean and rebuild:
-
Verify path mappings: The root
tsconfig.json includes these mappings for tsx:
LanceDB Native Module Errors
Problem: LanceDB native bindings missing for your platform.
Symptoms:
Solutions:
-
Intel Macs (darwin-x64):
- The project is configured to use LanceDB
0.22.3 (last version with Intel Mac support)
- This is set in
packages/core/package.json
- If you see this error, run:
pnpm install --force
-
Apple Silicon Macs (darwin-arm64):
- Should work with LanceDB
0.26.2+
- If issues persist, try:
pnpm install --force
-
Linux/Windows:
- LanceDB should auto-install the correct native binding
- Run:
pnpm install --force if needed
-
Check what’s installed:
Configuration Errors
Problem: Missing required environment variables.
Symptoms:
Solution:
-
Copy the example file:
-
Edit
.env and fill in required values:
-
Get your Telegram user ID:
- Message @userinfobot on Telegram
- Add your ID to
ALLOWED_USER_IDS
-
Verify the file loads (Node 20.6+):
Runtime Issues
Telegram Bot Conflicts
Problem: Multiple bot instances trying to poll Telegram simultaneously.
Symptoms:
Solution:
Only one instance can poll Telegram updates at a time. The conflict can come from:
- Local processes on your machine
- Remote deployments (VPS, cloud instances, etc.)
- Docker containers
- Another developer’s machine using the same bot token
Step 1: Check local processes
Step 2: Check Docker containers
Step 3: Check bot webhook status
Step 4: Clear webhook and pending updates
Step 5: Check remote deployments
If you deployed to a remote server, check there:
Step 6: Wait for timeout
If another instance was recently stopped, Telegram may still have an active long-polling connection (30 second timeout). Wait 30-60 seconds then try again.
Step 7: Stop all instances
If the conflict persists:
For Production: Use webhooks instead of long-polling to avoid conflicts:
Configure in .env:
Note: Implementing webhook support requires code changes in packages/telegram/src/index.ts.
Redis Connection Errors
Problem: Cannot connect to Redis.
Symptoms:
Solutions:
-
Use the Redis management script (recommended):
-
Manual platform-specific start (if script fails):
-
Check Redis URL in
.env:
-
Verify Redis is running:
Note: Redis is required for EchOS. If Redis is not running at startup, EchOS will exit with an error.
Database/Storage Errors
Problem: SQLite or LanceDB initialization failures.
Symptoms:
Solutions:
-
Create data directories:
-
Check permissions:
-
Check paths in
.env:
-
Clean state (CAUTION: deletes all data):
docker compose ps shows echos as unhealthy even though it’s working
Problem: Prior to this fix, the Docker healthcheck did fetch('http://localhost:3000/health'), which is served by the web interface (packages/web). If ENABLE_WEB=false (e.g. Telegram-only deployments), nothing ever listens on port 3000, so the healthcheck failed permanently — even on a perfectly healthy container. This made docker compose ps useless for spotting real problems, since “unhealthy” was the permanent baseline rather than a meaningful signal.
Fix: The healthcheck now checks a liveness heartbeat file (/tmp/echos-heartbeat, written every 15s by src/heartbeat.ts) instead of the web interface’s /health endpoint. This works regardless of ENABLE_WEB/ENABLE_TELEGRAM/ENABLE_MCP and still catches a wedged event loop (a hung process stops writing the heartbeat, same as it would stop responding to HTTP).
If you’re still seeing “unhealthy” after upgrading:
On low-memory hosts (e.g. Oracle Cloud’s free-tier VM.Standard.E2.1.Micro, 1GB RAM / no swap), a wedged-but-still-”Running” container after an OOM kill is the most common root cause. Check dmesg -T | grep -i oom on the host for confirmation, and add swap so transient spikes (e.g. the backup job’s tar/cp work) don’t trigger the OOM-killer:
Development Issues
TypeScript Errors
Problem: Type errors when editing code.
Solutions:
-
Check all packages:
-
Rebuild after package changes:
-
Use watch mode during development:
Vitest Test Failures
Problem: Tests failing or hanging.
Solutions:
-
Run tests:
-
Run specific test file:
-
Watch mode:
Storage Sync Issues
Manually added/edited markdown file not appearing in search
Problem: You added a .md file directly to the knowledge/ directory (or edited one in an external editor), but the agent can’t find it via search.
How sync works:
- On startup, EchOS reconciles all markdown files with SQLite and LanceDB automatically
- While running, a file watcher picks up any
add, change, or unlink events in real time (debounced 500 ms)
If a file isn’t being found:
-
Check the file has a valid
id in frontmatter — files without an id field are silently skipped:
-
If you added the file while the app was stopped — just restart. The startup reconciler will pick it up:
-
If you added the file while the app is running — the file watcher should index it within ~1 second. If it doesn’t appear after a few seconds, check the logs:
-
Enable debug logging to see all reconciler/watcher events:
Search results show stale content after editing a note externally
Problem: You edited a markdown file in VS Code or Obsidian, but the agent still returns the old content.
The content hash prevents unnecessary re-indexing, so re-indexing only happens when the body text of the note changes (not just frontmatter). If the watcher picked up the change but content looks stale, wait a moment for the debounce window (500 ms) then retry.
If the issue persists after restarting the app, check that the file has a valid id in its frontmatter — files without an ID are skipped by both the reconciler and the watcher.
Memory Issues
Memory stored but not recalled after /reset
Problem: You told the agent to remember something, then after /reset it doesn’t know the fact.
How memory works:
remember_about_me stores facts permanently in SQLite (survives /reset)
- On every new session, the top 15 memories by confidence are injected into the system prompt automatically
- Additional memories beyond the top 15 are searchable via
recall_knowledge
If a memory isn’t being recalled:
- It may be beyond position 15 — ask explicitly: “recall what you know about X” to trigger
recall_knowledge
- The search uses keyword matching — use related terms: “recall what you know about my birthday” or “recall birth year”
- Increase confidence when storing important facts: the agent can be told “remember this with high confidence”
To see all stored memories: ask “list everything you remember about me” — the agent will use recall_knowledge to retrieve all entries.
Content Status Issues
Article shows up in knowledge search even though I haven’t read it:
- Articles saved via
save_article start with status: saved (reading list), not status: read (knowledge)
- If the agent is mixing them, remind it: “distinguish between saved articles and things I’ve actually read”
- You can filter: “show only what I’ve actually read about X”
Reading list shows nothing:
- Ask: “show my reading list” — the agent calls
list_notes(status="saved")
- Articles saved before this feature was introduced may have
status: null — they won’t appear in filtered lists; use update_note or mark_content to set their status
Agent marks article as read when I didn’t ask:
- This is intentional: when you begin actively discussing a saved article, the agent auto-marks it
read
- To prevent this, tell the agent you’re just asking about the topic in general, not discussing that specific article
save_conversation creates too much noise:
save_conversation is only called when you explicitly ask (“save this conversation” or “save what we discussed about X”)
- It is never called automatically
CLI Issues (pnpm echos)
Empty response — agent runs but prints nothing
Most likely cause: The configured model is deprecated and the API returns an empty response.
Check: Look for a deprecation warning in the output. If you see The model '...' is deprecated, update DEFAULT_MODEL in .env to a current model (e.g. claude-haiku-4-5-20251001).
Debug: Run with logging enabled to see what happens:
Startup logs cluttering the output
By default the CLI suppresses all logs below warn. If you’re seeing INFO logs:
- Check whether
LOG_LEVEL is exported in your shell: echo $LOG_LEVEL
- Unset it or set it to
warn: unset LOG_LEVEL
Shell parse errors (zsh: parse error near '\n')
Special characters (?, >, *, !, &) in arguments are interpreted by the shell before echos sees them. Always quote arguments:
History not persisting between sessions
The history file is ~/.echos_history. If it doesn’t persist:
- Check write permissions:
ls -la ~/.echos_history
- The file is created on the first clean exit (
exit / quit / Ctrl+D). Kills via kill -9 skip the save.
YouTube Transcript Issues
”Unable to save this video” / YouTube transcript fails
Problem: The YouTube plugin uses youtube-transcript-api-js (pure JS) to fetch transcripts. Failures are usually caused by the server IP being blocked by YouTube.
Symptoms:
- Bot responds with “I was unable to save this video”
- Logs show:
YouTube transcript unavailable [IpBlocked] or YouTube transcript unavailable [RequestBlocked]
Fixes:
-
Local machine: No setup required — home IPs are not blocked by YouTube.
-
Docker / VPS: The Docker image includes all required JS dependencies. No extra steps needed.
Proxy for cloud deployments:
YouTube blocks most cloud/datacenter IPs. Configure a Webshare rotating proxy in .env:
Use your base Webshare username — without the -rotate suffix. The app appends -rotate automatically to enable IP rotation. If you copy the username directly from the Webshare dashboard and it already ends in -rotate, remove that suffix before pasting it here.
When set, transcript requests and audio downloads route through p.webshare.io:80.
Whisper transcribes audio in the wrong language
Problem: When a YouTube video has no captions and falls back to Whisper transcription, or when you send a Telegram voice message, Whisper may misidentify the language — e.g. transcribing English audio as Welsh or another language.
Symptoms:
- Transcript output is garbled or in the wrong language
- Voice message responses don’t match what you said
Fix: Pin the transcription language by setting WHISPER_LANGUAGE in your .env:
This accepts an ISO-639-1 two-letter code (e.g. en, fr, de, es, it, ja). When set, Whisper skips auto-detection and transcribes in the specified language. If unset, Whisper auto-detects (which can fail on short clips or accented speech).
Problem: The Twitter plugin uses the free FxTwitter API (api.fxtwitter.com). If the tweet is unavailable or the API is temporarily down, the save will fail.
Symptoms:
- Agent responds with “I wasn’t able to save that tweet” or similar
- Logs show:
save_tweet failed with an error message
Fixes:
-
Check the URL format — the plugin accepts these formats:
https://twitter.com/<user>/status/<id>
https://x.com/<user>/status/<id>
https://mobile.twitter.com/<user>/status/<id>
https://fxtwitter.com/<user>/status/<id> / https://vxtwitter.com/<user>/status/<id>
-
Check that the tweet exists and is public — private/protected accounts and deleted tweets cannot be fetched.
-
Enable debug logging to see the full error from the FxTwitter API:
-
FxTwitter rate limiting — the FxTwitter API is a free public service and may occasionally rate-limit requests. Wait a moment and try again.
Problem: The agent creates a generic note when given a Twitter URL instead of calling save_tweet.
Fix: The system prompt includes explicit URL routing. If this happens, it may indicate the system prompt wasn’t applied to the session. Try resetting the session:
- Telegram: Send
/reset
- CLI: Exit and restart (
exit or Ctrl+D)
The routing rules in the system prompt direct twitter.com and x.com URLs to save_tweet, not create_note.
MCP Server Issues
MCP server not starting
Problem: ENABLE_MCP=true is set but no “MCP server started” log line appears.
Solution: Check for a port conflict on MCP_PORT (default 3939):
Kill the conflicting process or change MCP_PORT in .env, then restart the daemon.
Claude Code shows the MCP server as disconnected
Problem: The echos server appears in Claude Code but shows as disconnected or errors on use.
Solution:
- Confirm the daemon is running (
pnpm start or check your service manager).
- Verify the URL in
.claude.json matches MCP_PORT in .env.
- If
MCP_API_KEY is set, confirm the Authorization header value matches exactly.
Unauthorized errors from the MCP server
Problem: Requests return 401 Unauthorized.
Solution: The bearer token in your client config does not match MCP_API_KEY. Regenerate and update both:
Getting Help
If you’re still stuck:
-
Check the logs: EchOS uses Pino for structured logging
-
Enable debug logging: Set
LOG_LEVEL=debug in .env
-
Check system requirements:
- Node.js 20+ (
node --version)
- pnpm 9+ (
pnpm --version)
- Redis running (
redis-cli ping)
- Enough disk space for embeddings/vectors
-
Review security docs: See Security
-
Architecture overview: See Architecture