santifer/career-ops · error · Error

H1B_API_BASE must use https, or http on loopback: ${trimmed}

Error message

H1B_API_BASE must use https, or http on loopback: ${trimmed}

What it means

resolveBase() enforces a transport-safety policy: H1B_API_BASE must be https, except plain http is allowed on loopback hosts (localhost, 127.x, ::1) so a self-hoster can develop against a local worker. Exempting every scheme on loopback previously let ftp://localhost and ws://localhost through, which later died as an opaque 'fetch failed'.

Source

Thrown at plugins/h1b-sponsor/lib/api.mjs:71

    // Undici refuses a credentialed Request anyway, and the value reaches
    // stdout through the source field, so this would print a password.
    throw new Error('H1B_API_BASE must not embed credentials.');
  }
  if (parsed.search || parsed.hash) {
    // Paths are appended, so a query or fragment swallows them: the request
    // would go to the base itself and answer about a company never asked for.
    throw new Error('H1B_API_BASE must not contain a query string or a fragment.');
  }
  // Plain http would put an Authorization header on the wire in the clear.
  // Loopback is exempt so a self-hoster can develop against a local worker,
  // but only for http. Exempting every scheme on a loopback host let
  // ftp://localhost and ws://localhost past validation, and those die later
  // inside fetch as a bare "fetch failed", which is the opaque failure this
  // check exists to replace with a named configuration error.
  const loopback = /^(localhost|127\.\d+\.\d+\.\d+|\[::1\]|::1)$/i.test(parsed.hostname);
  const allowedScheme = parsed.protocol === 'https:' || (parsed.protocol === 'http:' && loopback);
  if (!allowedScheme) {
    throw new Error(`H1B_API_BASE must use https, or http on loopback: ${trimmed}`);
  }
  // Appended as `${base}/employers/...`, so a trailing slash would double up.
  return trimmed.replace(/\/+$/, '');
}
const USER_AGENT = 'career-ops-plugin-h1b-sponsor/1.0';
const DEFAULT_TIMEOUT_MS = 10_000;
const MAX_RETRY_WAIT_MS = 10_000;
const MAX_REDIRECTS = 3;
// The largest real employer profile this API serves is about 13 KB and a search
// page is about 4 KB, so a megabyte is ~80x headroom for future growth while
// still refusing to buffer a runaway or hostile body.
const MAX_READ_BYTES = 1024 * 1024;

function buildHeaders(token) {
  const h = { 'Accept': 'application/json', 'User-Agent': USER_AGENT };
  if (typeof token === 'string' && token.trim()) {
    h['Authorization'] = `Bearer ${token.trim()}`;
  }

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Serve the API over https and set H1B_API_BASE to the https:// URL.
  2. For local development, use http://localhost:PORT or http://127.0.0.1:PORT — loopback http is explicitly allowed.
  3. Put TLS in front of a plain-http internal server (reverse proxy with a certificate) instead of relaxing the scheme.
  4. Do not use ws://, ftp://, or other schemes — fetch only speaks http(s).

Example fix

// before (.env)
H1B_API_BASE=http://api.staging.example.com
// after
H1B_API_BASE=https://api.staging.example.com
// or, for local dev:
// H1B_API_BASE=http://localhost:8787
Defensive patterns

Strategy: validation

Validate before calling

const LOOPBACK = /^(localhost|127\.\d+\.\d+\.\d+|\[::1\]|::1)$/i;
function isAllowedScheme(v) {
  try {
    const u = new URL(v);
    return u.protocol === 'https:' || (u.protocol === 'http:' && LOOPBACK.test(u.hostname));
  } catch { return false; }
}
// if (!isAllowedScheme(process.env.H1B_API_BASE)) fix before running;

Type guard

function isSecureOrLoopback(v) {
  try {
    const u = new URL(v);
    return u.protocol === 'https:' ||
      (u.protocol === 'http:' && /^(localhost|127\.|\[::1\]|::1)/i.test(u.hostname));
  } catch { return false; }
}

Try / catch

try {
  await lookupEmployer(name);
} catch (e) {
  if (e.message.startsWith('H1B_API_BASE must use https')) {
    console.error('Use https:// in production; plain http only for localhost/127.x dev.');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Setting H1B_API_BASE to an http:// URL with a non-loopback hostname (e.g. http://api.example.com), or to a non-http(s) scheme entirely (ftp:, ws:), then calling any plugin API function.

Common situations: Pointing at an internal staging server that lacks TLS; accidentally leaving 'http://' from a local dev config when switching to production; using ws:// or ftp:// because the docs snippet was copied from another service.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/0d5883c88037c9bd. Report an issue: GitHub.