April 16, 2026 • Version: 2026.2.6-3

Hinzufügen eines maxMessageAge-Filters zum Verwerfen veralteter erneut zugestellter BlueBubbles-Webhooks - Adding maxMessageAge Filter to Drop Stale Re-delivered BlueBubbles Webhooks

Konfiguration des BlueBubbles-Kanals von OpenClaw, um eingehende Nachrichten, die älter als ein konfigurierbarer Schwellenwert sind, automatisch zu verwerfen. Dadurch wird die Verarbeitung von veralteten Webhooks und internen Fehlerantworten verhindert.

🔍 Symptome

Beobachtetes Verhalten

Wenn BlueBubbles alte Nachrichten-Webhooks erneut zustellt, treten folgende Symptome auf:

  • Veraltete Nachrichtenverarbeitung: Nachrichten mit Zeitstempeln, die Stunden oder Tage in der Vergangenheit liegen, erscheinen im Gesprächsfluss des Agenten, als wären sie neu
  • Fehlerhafte Antworten: Der Agent generiert kontextbezogene Antworten auf uralte Nachrichten, was Benutzer verwirrt
  • Rate-Limit-Fehler-Antworten: Wenn der KI-Dienst eine 529 (Rate-Limit) Antwort zurückgibt, wird der rohe Fehlertext als iMessage-Antwort zurückgesendet:
    The AI service is temporarily overloaded. Please try again in a moment.

Technische Manifestationen

Die problematischen Webhook-Nutzdaten enthalten dateCreated-Werte, die nicht mit der aktuellen Zeit übereinstimmen:

json { “id”: “msg-abc123”, “dateCreated”: “2026-01-15T08:30:00Z”, “text”: “Hello from yesterday”, “guid”: “some-guid” }

Wenn die aktuelle Serverzeit 2026-02-24T14:00:00Z beträgt, ist diese Nachricht 40 Tage alt, wird aber dennoch durch die Standard-Eingangs-Handler-Pipeline verarbeitet.

🧠 Ursache

Architektonisches Problem

Dem BlueBubbles-Eingangshandler fehlt eine zeitliche Validierung auf der Nachrichtenannahme-Schicht. Die Webhook-Verarbeitungspipeline weist zwei kritische Lücken auf:

  1. Keine Altersschwellenwert-Validierung: Der Handler verarbeitet jede eingehende Nachricht unabhängig von ihrem `dateCreated`-Zeitstempel. BlueBubbles liefert gelegentlich Nachrichten aus seiner internen Warteschlange erneut aus und sendet Nutzdaten mit Zeitstempeln, die weit in der Vergangenheit liegen, als wären sie frische eingehende Nachrichten.
  2. Interne Fehlermeldungs-Leckage: Fehlermeldungen, die von der internen Fehlerbehandlungsmiddleware generiert werden (insbesondere 529 Rate-Limit-Antworten), werden durch dieselbe Antwort-Pipeline wie legitime Benutzernachrichten geleitet, wodurch roher Fehlertext als iMessage-Antworten gesendet wird.

Fehlersequenz

  1. BlueBubbles erkennt eine alte Nachricht in seiner Warteschlange
  2. BlueBubbles sendet Webhook POST an den OpenClaw-Eingangsendpunkt
  3. Handler extrahiert Nachricht, extrahiert dateCreated: “2026-01-15T08:30:00Z”
  4. KEINE Altersprüfung durchgeführt → Nachricht tritt in die Agenten-Verarbeitungspipeline ein
  5. Agent generiert Antwort basierend auf veraltetem Kontext
  6. Antwort wird über den BlueBubbles-Antwortkanal als iMessage gesendet

ODER (für Fehlerfall):

  1. KI-Dienst gibt 529 Rate-Limit-Fehler zurück
  2. Fehler-Middleware generiert menschenlesbaren Fehlertext
  3. Fehlertext fließt durch Antwortkanal, anstatt protokolliert zu werden
  4. Benutzer erhält “The AI service is temporarily overloaded…” als Textnachricht

Code-Pfad-Analyse

Das Problem befindet sich in der Nachrichtenannahme-Logik innerhalb des BlueBubbles-Handlers. Ohne ein konfigurierbares maxMessageAgeSec-Feld gibt es keinen Mechanismus zum:

  • Vergleichen des `dateCreated` der Nachricht mit der aktuellen Serverzeit
  • Bestimmen, ob die verstrichene Zeit einen konfigurierten Schwellenwert überschreitet
  • Kurzschließen der Verarbeitungspipeline mit einem stillen Verwerfen (protokolliert auf DEBUG-Ebene)

🛠️ Schritt-für-Schritt-Lösung

Konfigurationsergänzung

Fügen Sie das maxMessageAgeSec-Feld zu Ihrer OpenClaw-Konfigurationsdatei (config.json oder umgebungsbasierter Konfiguration) hinzu:

json { “channels”: { “bluebubbles”: { “serverUrl”: “https://your-bluebubbles-server.local”, “password”: “your-password-here”, “maxMessageAgeSec”: 300 } } }

Konfiguration vorher vs. nachher

Vorher (keine Altersfilterung):

json { “channels”: { “bluebubbles”: { “serverUrl”: “https://bb-server.local”, “password”: “secret123” } } }

Nachher (mit Altersfilterung):

json { “channels”: { “bluebubbles”: { “serverUrl”: “https://bb-server.local”, “password”: “secret123”, “maxMessageAgeSec”: 300 } } }

Schritte zur Handler-Implementierung

Um diese Lösung in der Codebasis zu implementieren, führen Sie folgende Änderungen durch:

  1. Definieren Sie die Validierungskonstante in der Kanal-Konfigurationsschnittstelle:
    // Within channels/bluebubbles/types.ts or similar
    export interface BlueBubblesConfig {
      serverUrl: string;
      password: string;
      maxMessageAgeSec?: number;  // Optional, defaults to no filtering
    }
  2. Fügen Sie die Altersvalidierung im eingehenden Nachrichtenhandler hinzu:
    // Within the webhook handler function
    async function handleInboundMessage(payload: BlueBubblesWebhookPayload): Promise<void> {
      const config = getBlueBubblesConfig();
    

    // Validate message age if (config.maxMessageAgeSec) { const messageAge = Date.now() - new Date(payload.dateCreated).getTime(); const maxAgeMs = config.maxMessageAgeSec * 1000;

    if (messageAge > maxAgeMs) {
      logger.debug(`Dropping stale message ${payload.guid} (age: ${Math.floor(messageAge/60000)}m)`);
      return;  // Silent drop
    }
    

    }

    // Continue with normal processing… await processMessage(payload); }

  3. Stellen Sie sicher, dass Fehlermeldungen niemals an Antwortflächen weitergeleitet werden:
    // Error middleware or handler
    function handleAIErrors(error: Error, context: MessageContext): void {
      if (error.statusCode === 529) {
        // Rate limit: log internally, do NOT send to user
        logger.warn(`AI service rate-limited for message ${context.messageId}`);
        return;  // No reply sent
      }
    

    // For other errors, decide based on error visibility config if (shouldExposeErrors()) { sendReply(context, generateSafeErrorMessage(error)); } else { logger.error(error); } }

🧪 Verifizierung

Testverfahren für Nachrichtenalter-Filterung

  1. Stellen Sie die aktualisierte Konfiguration bereit und starten Sie den OpenClaw-Dienst neu:
    # Restart OpenClaw to load new configuration
    sudo systemctl restart openclaw
    

    Check service status

    sudo systemctl status openclaw –no-pager

  2. Verifizieren Sie, dass die Konfiguration geladen wurde:
    # Check logs for config load
    tail -50 /var/log/openclaw/openclaw.log | grep -i "bluebubbles\|maxMessageAge"
    

    Expected output:

    [INFO] BlueBubbles channel initialized with maxMessageAgeSec=300

  3. Testen Sie die Handhabung veralteter Nachrichten:
    # Send a test webhook with old timestamp via curl
    curl -X POST http://localhost:3000/webhooks/bluebubbles \
      -H "Content-Type: application/json" \
      -H "X-BlueBubbles-Password: your-password" \
      -d '{
        "id": "test-stale-001",
        "guid": "test-stale-guid",
        "dateCreated": "2026-01-15T08:30:00Z",
        "text": "Test stale message"
      }'
    

    Expected: 200 OK, no reply sent, log entry for dropped message

    Check logs for dropped message confirmation

    tail -20 /var/log/openclaw/openclaw.log | grep “Dropping stale”

  4. Testen Sie die Handhabung frischer Nachrichten (Sanity-Check):
    # Send a webhook with current timestamp
    curl -X POST http://localhost:3000/webhooks/bluebubbles \
      -H "Content-Type: application/json" \
      -H "X-BlueBubbles-Password: your-password" \
      -d "{
        \"id\": \"test-fresh-001\",
        \"guid\": \"test-fresh-guid-$(date +%s)\",
        \"dateCreated\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
        \"text\": \"Test fresh message\"
      }"
    

    Expected: 200 OK, message processed normally, iMessage reply sent

Erwartete Ergebnisse

  • Veraltete Nachrichten (älter als `maxMessageAgeSec`) geben HTTP 200 zurück, erzeugen aber einen DEBUG-Level-Protokolleintrag; keine Antwort wird gesendet
  • Frische Nachrichten werden durch die normale Pipeline verarbeitet und erhalten Agenten-Antworten
  • Fehlermeldungen (529 Rate-Limit) werden protokolliert, aber niemals als iMessage-Antworten weitergeleitet

⚠️ Häufige Fehler

  • Zeitzonen-Mismatch: Stellen Sie sicher, dass die Systemuhr des OpenClaw-Servers mit BlueBubbles synchronisiert ist. Wenn sich Server in verschiedenen Zeitzonen befinden und lokale Zeit für `dateCreated` verwenden, können Nachrichten fälschlicherweise als veraltet oder frisch klassifiziert werden. Verwenden Sie durchgängig UTC-Zeitstempel.
  • Konfiguration nicht neu gestartet: Änderungen an `config.json` werden erst wirksam, wenn OpenClaw neu gestartet wird. Hot-Reload wird für strukturelle Konfigurationsänderungen nicht unterstützt.
  • Konfligierende Überlastungsverhalten: In macOS-Umgebungen verwendet der `launchctl`-Dienst möglicherweise nicht `systemctl`. Verwenden Sie statt `systemctl restart` die Befehle `launchctl unload` / `launchctl load`.
  • Standardverhalten ohne Konfiguration: Wenn `maxMessageAgeSec` weggelassen wird, werden alle Nachrichten (einschließlich veralteter) verarbeitet. Es gibt kein eingebautes Standardverhalten; Sie müssen den Wert explizit setzen.
  • Docker-Umgebungsvariablen-Mapping: Bei Verwendung von Docker müssen verschachtelte JSON-Objekte in Umgebungsvariablen doppelte Unterstriche für die Hierarchie verwenden:
    CHANNELS__BLUEBUBBLES__MAXMESSAGEAGESEC=300
  • Log-Level-Ausführlichkeit: DEBUG-Level-Drop-Meldungen werden in der Standard-Protokollausgabe möglicherweise nicht angezeigt. Stellen Sie sicher, dass Ihre Protokollierungskonfiguration für den BlueBubbles-Kanal auf DEBUG gesetzt ist, oder überprüfen Sie die Trace-Protokolldatei.

🔗 Zugehörige Fehler

  • 529 Rate-Limit-Fehler: Vorübergehend überlastete KI-Dienst-Antworten, die als iMessage-Text durchsickern, wenn die Fehlerbehandlung nicht von der Antwort-Pipeline isoliert ist
  • EAI_AGAIN / EHOSTUNREACH: Netzwerkfehler, wenn der BlueBubbles-Server nicht erreichbar ist; diese unterscheiden sich von Nachrichtenalter-Problemen, können aber in ähnlichen Protokollen erscheinen
  • Doppelte Nachrichtenverarbeitung: BlueBubbles sendet gelegentlich denselben Webhook mehrmals; in Kombination mit fehlender Altersfilterung führt dies zu doppelten Agenten-Antworten
  • Authentifizierungsfehler (401): Falsches Passwort in der Konfiguration kann dazu führen, dass alle Webhooks abgelehnt werden; nicht mit der Altersfilterung zusammenhängend, wird aber möglicherweise für Konfigurationsfehler gehalten
  • Zeitstempel-Parsing-Fehler: Fehlerhafte `dateCreated`-Felder in BlueBubbles-Nutzdaten können Ausnahmen verursachen; stellen Sie eine graceful Handhabung ungültiger Datumsformate sicher

Belege & Quellen

Diese Troubleshooting-Anleitung wurde automatisch von der FixClaw Intelligence Pipeline aus Community-Diskussionen synthetisiert.