ruvnet/ruflo · error

peer url is not a valid URL

Error message

peer url is not a valid URL

What it means

validatePeerUrl rejects peer URLs that the URL constructor cannot parse at all. The library requires each federation peer entry to carry a syntactically valid absolute URL, since the value is later used as a network endpoint. Malformed strings are refused at registration time rather than failing later during requests.

Solutions

  1. Include the full absolute URL with scheme, e.g. 'https://peer.example.com:8080'
  2. Trim whitespace and remove stray quotes/characters from the configured URL
  3. Validate with new URL(raw) in the caller before persisting peer config
  4. Check the config source (env var, JSON, YAML) isn't truncating or interpolating the value incorrectly

Example fix

// before
addPeer(base, { nodeId: '0a1b2c3d4e5f6071', url: 'peer.example.com', publicKey: PK });
// after
addPeer(base, { nodeId: '0a1b2c3d4e5f6071', url: 'https://peer.example.com', publicKey: PK });
Defensive patterns

Strategy: validation

Validate before calling

function isParsableUrl(raw: string): boolean {
  try { new URL(raw); return true; } catch { return false; }
}
if (!isParsableUrl(peerUrl)) throw new Error(`peer url must be an absolute URL with scheme: ${peerUrl}`);

Type guard

function isHttpUrl(raw: string): raw is string {
  try { const u = new URL(raw); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; }
}

Try / catch

try {
  const origin = validatePeerUrl(rawUrl);
} catch (e) {
  if (/peer url/.test(e.message)) {
    console.error(`Invalid peer URL '${rawUrl}': ${e.message}`);
    // prompt user / abort registration
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validatePeerUrl(raw) or addPeer(basePath, {..., url: raw}) with a string for which new URL(raw) throws — e.g. 'peer.example.com' (no scheme), 'http://' (empty host), 'not a url', or an empty string.

Common situations: Config files where the scheme was omitted; templated URLs left with an unfilled placeholder; trailing spaces or stray characters pasted into a peer list; YAML/JSON values coerced from numbers.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-09-15). Data as JSON: /api/errors/b8578640ce258a4a. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/mcp-tools/agentbbs-federation.ts:252

  } catch {
    return [];
  }
}

function writePeers(basePath: string, peers: FederationPeer[]): void {
  ensureDir(basePath);
  writeFileSync(peersPath(basePath), JSON.stringify(peers, null, 2) + '\n');
}

/**
 * Reject anything that is not a plain http(s) URL to a host.
 *
 * Blocks credentials-in-URL (they would be logged), and non-http schemes such
 * as `file:` which would turn a peer entry into a local file read.
 */
export function validatePeerUrl(raw: string): string {
  let u: URL;
  try { u = new URL(raw); } catch { throw new Error('peer url is not a valid URL'); }
  if (u.protocol !== 'http:' && u.protocol !== 'https:') {
    throw new Error('peer url must be http or https');
  }
  if (u.username || u.password) throw new Error('peer url must not embed credentials');
  return u.origin;
}

export function addPeer(
  basePath: string,
  input: { nodeId: string; url: string; publicKey: string; label?: string },
): FederationPeer {
  if (!NODE_ID_RE.test(input.nodeId ?? '')) throw new Error('nodeId must be 16 lowercase hex chars');
  if (!HEX64_RE.test(input.publicKey ?? '')) throw new Error('publicKey must be 64 lowercase hex chars');
  const url = validatePeerUrl(String(input.url));

  const peers = readPeers(basePath);
  if (peers.length >= MAX_PEERS) throw new Error(`peer registry is full (${MAX_PEERS})`);

View on GitHub (pinned to 2602b642d9)