decolua/9router · warning

[Tunnel] kill warn: ${e.message}

Error message

[Tunnel] kill warn: ${e.message}

What it means

disableTunnel tries to kill the active cloudflared process via killCloudflared(activeLocalPort). If that throws (process already gone, permission denied, missing pid info, signal error), it logs '[Tunnel] kill warn: <message>' and continues teardown: clears the pid file, clears state, and marks the tunnel disabled in settings. The kill failure alone does not fail the disable operation.

Source

Thrown at src/lib/tunnel/cloudflare/manager.js:119

    return { success: true, tunnelUrl, shortId, publicUrl };
  } catch (e) {
    // Suppress noise when spawn was deliberately killed (restart/disable superseded it)
    if (!/cloudflared killed|tunnel cancelled/.test(e.message)) {
      console.error(`[Tunnel] enable error: ${e.message}`);
    }
    throw e;
  } finally {
    svc.spawnInProgress = false;
  }
}

export async function disableTunnel() {
  console.log("[Tunnel] disable");
  // Abort any in-flight enable so it cannot resurrect state after we clear it
  svc.cancelToken.cancelled = true;
  setUnexpectedExitHandler(null);

  try { killCloudflared(svc.activeLocalPort); } catch (e) { console.warn(`[Tunnel] kill warn: ${e.message}`); }
  clearPid();

  const state = loadState();
  if (state) saveState({ shortId: state.shortId, tunnelUrl: null });

  await updateSettings({ tunnelEnabled: false, tunnelUrl: "" });
  // Force-clear flags so a subsequent enable is not blocked by a stuck spawnInProgress
  svc.spawnInProgress = false;
  svc.activeLocalPort = null;
  return { success: true };
}

export async function getTunnelStatus() {
  const settings = await getSettings();
  const settingsEnabled = settings.tunnelEnabled === true;
  const state = loadState();
  const shortId = state?.shortId || "";
  const publicUrl = shortId ? `https://r${shortId}.abc-tunnel.us` : "";

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Ignore if state shows tunnel disabled — disable succeeded and the process was likely already dead.
  2. Check for orphaned cloudflared processes manually (ps aux | grep cloudflared) and kill them.
  3. Clear the stale pid/state files in the data dir and re-run disableTunnel.
  4. Fix permission context (run disable as the same user that spawned cloudflared).
Defensive patterns

Strategy: try-catch

Validate before calling

// check for a live cloudflared before/after disable
import { execSync } from "child_process";
const alive = (() => { try { return execSync("pgrep -f cloudflared", { stdio: "pipe" }).toString().trim(); } catch { return ""; } })();

Try / catch

// the library already swallows the kill error; mirror that in userland
try {
  await disableTunnel();
} catch (e) {
  console.warn("disable failed:", e.message);
}

Prevention

When it happens

Trigger: killCloudflared throws while disabling: cloudflared already exited, pid file points at a dead/foreign PID (EPERM), the process is owned by another user, or the platform-specific kill command is unavailable.

Common situations: Double-disable races; cloudflared crashed earlier (so kill has nothing to kill); Docker/containers restricting signals; Windows process-permission issues; stale pid file after a reboot with PID reuse.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/1a2a3cec344c1f9a. Report an issue: GitHub.