affaan-m/ECC · error · Error

[ECC] ECC_DASHBOARD_HOST must be loopback-only (127.0.0.1…

Error message

[ECC] ECC_DASHBOARD_HOST must be loopback-only (127.0.0.1, localhost, or ::1).

What it means

resolveDashboardHost in scripts/dashboard-web.js enforces that the dashboard HTTP server binds only to loopback addresses. ECC_DASHBOARD_HOST set to anything other than 127.0.0.1, localhost, or ::1 throws, preventing accidental exposure of the dashboard to the network. This is a deliberate security-by-default measure.

Solutions

  1. Set ECC_DASHBOARD_HOST to 127.0.0.1, localhost, or ::1 (default 127.0.0.1 is used when unset).
  2. If remote access is needed, put a proper reverse proxy with auth in front of the loopback-bound server instead of rebinding.
  3. Check .env / shell exports for stray whitespace or casing and normalize the value.

Example fix

// before
ECC_DASHBOARD_HOST=0.0.0.0 node scripts/dashboard-web.js
// after
ECC_DASHBOARD_HOST=127.0.0.1 node scripts/dashboard-web.js
Defensive patterns

Strategy: validation

Validate before calling

const host = (process.env.ECC_DASHBOARD_HOST || '').trim().toLowerCase();
if (host && !['127.0.0.1', 'localhost', '::1'].includes(host)) {
  throw new Error(`ECC_DASHBOARD_HOST must be loopback, got: ${host}`);
}

Type guard

function isLoopbackHost(v) { return ['127.0.0.1','localhost','::1','[::1]'].includes(String(v).trim().toLowerCase()); }

Try / catch

try {
  startDashboard();
} catch (e) {
  if (e.message.includes('loopback-only')) {
    console.error('Unset ECC_DASHBOARD_HOST or set it to 127.0.0.1 to use the dashboard locally.');
    process.exit(1);
  } else throw e;
}

Prevention

When it happens

Trigger: Setting ECC_DASHBOARD_HOST to a non-loopback value such as 0.0.0.0, a LAN IP (192.168.x.x), a hostname, or an uppercase/untrimmed variant that still fails the loopback set check.

Common situations: Trying to expose the dashboard in Docker or to a phone on the LAN by setting 0.0.0.0; CI setting the host to a container hostname; trailing spaces or capitalization quirks in env files.

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/8c13ba3fc9ebb22c. Report an issue: GitHub.

Appendix: source

Thrown at scripts/dashboard-web.js:30

const fs = require('fs');
const path = require('path');
const http = require('http');
const {
  LOOPBACK_HOSTNAMES,
  buildAllowedHostnames,
  isAllowedHostHeader,
  isAllowedOrigin,
} = require('./lib/loopback-guard');
const { normalizeAgentTools } = require('./lib/agent-tools');
const { readHooksConfig } = require('./lib/hooks-config');

const DEFAULT_HOST = '127.0.0.1';

function resolveDashboardHost(env = process.env) {
  const configured = String(env.ECC_DASHBOARD_HOST || '').trim().toLowerCase();
  if (!configured) return DEFAULT_HOST;
  if (!LOOPBACK_HOSTNAMES.has(configured)) {
    throw new Error(
      '[ECC] ECC_DASHBOARD_HOST must be loopback-only ' +
      '(127.0.0.1, localhost, or ::1).'
    );
  }
  return configured === '[::1]' ? '::1' : configured;
}

function parsePort(v) {
  const n = parseInt(String(v), 10);
  if (isNaN(n) || n < 1 || n > 65535) { console.error('[ECC] Invalid port: ' + v + ' — using 3456'); return 3456; }
  return n;
}
const PORT = parsePort(process.argv[2] || process.env.ECC_DASHBOARD_PORT || '3456');
const HOST = resolveDashboardHost();
const ROOT = path.resolve(__dirname, '..');

function readFrontmatter(p) {
  try {

View on GitHub (pinned to 8321021c54)