Skip to main content
← Back to list
01Issue
FeatureClosedSwamp CLI
Assigneesstack72

Relationships

#1003 swamp serve: catch unhandled promise rejections from extensions

Opened by stack72 · 7/7/2026

Summary

swamp serve can be crashed by extension code that creates detached rejecting promises. The server should defensively catch unhandled promise rejections, log them, and fail the in-flight method/workflow cleanly — not terminate the process.

Motivation

swamp-club/swamp-extensions#999 reported that @adam/cfgmgmt crashes swamp serve when an SSH connection fails. The root cause is a common JavaScript footgun:

const promise = _establishConnection(opts, key, port);
inflight.set(key, promise);
promise.finally(() => inflight.delete(key));  // <-- detached rejecting promise
return promise;

Promise.prototype.finally() returns a new promise. When _establishConnection() rejects, the caller handles the original promise — but the promise returned by .finally() is detached and also rejects. Deno treats that as an uncaught rejection and can terminate the process.

This is an extension-side bug, but any extension can introduce it. The server should be resilient.

Proposed fix

Add a global unhandledrejection handler to swamp serve:

globalThis.addEventListener("unhandledrejection", (event) => {
  event.preventDefault();  // prevent Deno from terminating
  logger.error(
    "Unhandled promise rejection (likely from an extension). " +
    "The in-flight method will be marked as failed.",
    { reason: event.reason },
  );
  // If possible, correlate with the in-flight method/workflow
  // and mark it as failed rather than silently swallowing.
});

Design considerations

  • Don't silently swallow: log at error level so the rejection is visible in swamp serve output and any connected observability.
  • Correlate with the method: if the rejection can be associated with a running model method or workflow step, fail that unit of work with a clear error message referencing the unhandled rejection.
  • Keep the server alive: the whole point — one misbehaving extension method should not take down other in-flight work or the serve process itself.
  • Scope to swamp serve: the CLI (swamp model ... method run) can let Deno's default behavior terminate the process, since there's no long-running server to protect. This is specifically about the persistent serve mode.

Reproduction shape

  1. Run swamp serve.
  2. Trigger any extension method that creates a detached rejecting promise (e.g., @adam/cfgmgmt/systemd against an unreachable SSH host).
  3. After the rejection fires, the server exits with Uncaught (in promise) instead of failing the method cleanly.

Context

  • Original report: swamp-club/swamp-extensions#999 (closed — extension-side bug in @adam/cfgmgmt)
  • Reporter: @evrardjp
  • The @swamp/ssh extension is NOT affected (uses Deno.Command with try/finally, no inflight promise map).
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED+ 1 MOREASSIGNEDCLASSIFICATION

Closed

7/7/2026, 12:15:40 AM

No activity in this phase yet.

03Sludge Pulse
stack72 assigned stack727/7/2026, 12:11:18 AM
Editable. Press Enter to edit.

stack72 commented 7/7/2026, 12:15:40 AM

Duplicate of #1001 — same root cause (detached rejecting promise in extension code crashes swamp serve) and same desired fix (global unhandledrejection handler). Continuing work under #1001.

Sign in to post a ripple.