Files
prose-contracts/zulip-adapter-lessons.prose.md
T
root 4ee441f622 feat: zulip-adapter-lessons — document 8 known failure modes + deployment checklist
Documents lessons from building pi Zulip extension and Hermes Zulip plugin:
1. Queue registration content-type
2. Missing bot_user_id for @mentions
3. Zero stream subscriptions
4. Stale errors never cleared
5. Stuck detection too aggressive
6. Env var name mismatch
7. Poll interval too aggressive
8. A2A port conflict

Also: fixed Hermes adapter to clear stale errors on successful poll
2026-06-29 22:22:52 +00:00

3.7 KiB

kind, name, description
kind name description
pattern zulip-adapter-lessons Lessons learned from building and debugging the pi Zulip extension and Hermes Zulip plugin. Documents failure modes, fixes, and patterns that apply across all Zulip adapters.

Known Failure Modes & Fixes

1. Queue Registration Content-Type

  • Symptom: Event queue registered but delivers no stream events
  • Root cause: multipart/form-data (from isomorphic-form-data / form-data npm pkgs) vs application/x-www-form-urlencoded
  • Fix: Always use URLSearchParams / form-urlencoded for /api/v1/register
  • Applies to: pi extension (fixed), Hermes adapter (httpx sends form-urlencoded by default )

2. Missing bot_user_id for @mention Detection

  • Symptom: Bot receives stream messages but doesn't respond to @mentions
  • Root cause: /api/v1/register doesn't return user_id; mentioned_users check fails when bot ID is null
  • Fix: Call GET /api/v1/users/me after registration to get user_id
  • Applies to: pi extension (fixed), Hermes adapter (fixed via _resolve_bot_user_id() )

3. Zero Stream Subscriptions

  • Symptom: Bot can send to streams but never receives stream events
  • Root cause: Bot user created with 0 stream subscriptions — only DMs arrive
  • Fix: POST /api/v1/users/me/subscriptions with required stream names
  • Applies to: pi extension (fixed), Hermes adapter (⚠️ needs check)

4. Stale Errors Never Cleared

  • Symptom: Health monitor shows persistent error long after recovery
  • Root cause: last_error set on failure but never cleared on successful poll
  • Fix: Clear lastError on every successful poll (not just on reconnect)
  • Applies to: pi extension (fixed), Hermes adapter (⚠️ needs check)

5. Stuck Detection Too Aggressive

  • Symptom: Monitor restarts bot during quiet periods (nights/weekends)
  • Root cause: 30-min stuck threshold doesn't account for low traffic
  • Fix: Raise threshold to 4 hours — or remove stuck detection entirely
  • Applies to: pi extension (fixed to 4h), Hermes gateway (uses own idle detection)

6. Env Var Name Mismatch (ZULIP_URL vs ZULIP_SITE)

  • Symptom: Plugin not loading because check_fn returns False
  • Root cause: Some deploy configs use ZULIP_URL, adapter code expects ZULIP_SITE
  • Fix: Support both names in check_fn, validate_config, and env_enablement_fn
  • Applies to: Hermes adapter (fixed )

7. Poll Interval Too Aggressive

  • Symptom: Queue expires faster than expected, reconnection cycling
  • Root cause: 3s poll interval with dont_block=true creates many requests
  • Fix: Use long-poll (no dont_block) with 30s+ server timeout
  • Applies to: pi extension (uses long-poll ), Hermes adapter (uses long-poll )

8. A2A Server Address Already In Use

  • Symptom: A2A server fails to start with "[Errno 98] address already in use"
  • Root cause: Previous process holds port after container restart
  • Fix: Kill old process before starting, or use docker restart to clear state
  • Applies to: kagentz Agent Zero deployment

Deployment Checklist

When deploying a new Zulip adapter, verify:

  • Queue registered with form-urlencoded content type
  • Bot user_id fetched from /api/v1/users/me
  • Subscribed to all expected streams (check with /api/v1/users/me/subscriptions)
  • last_error cleared on successful poll
  • Env vars support both ZULIP_URL and ZULIP_SITE
  • Stream @mentions detected via mentioned_users array
  • @all-bots detected via configurable user_id
  • Poll uses long-poll (not dont_block=true polling)
  • Stuck/idle detection accounts for quiet periods