{"record":{"id":"2bcb631121c38ae5","repo":"nanocoai/nanoclaw","slug":"url-must-be-a-valid-http-s-url","errorCode":null,"errorMessage":"url must be a valid HTTP(S) URL","messagePattern":"url must be a valid HTTP\\(S\\) URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/container-config.ts","lineNumber":155,"sourceCode":"  }\n  if (type === 'stdio' && !command) throw new Error('type \"stdio\" requires command');\n  if (type === 'http' && !url) throw new Error('type \"http\" requires url');\n\n  const instructions = input.instructions;\n  if (instructions !== undefined && typeof instructions !== 'string') {\n    throw new Error('MCP instructions must be a string');\n  }\n\n  if (url !== undefined) {\n    if (command !== undefined) throw new Error('Provide exactly one of command or url');\n    if (input.args !== undefined || input.env !== undefined || input.cwd !== undefined) {\n      throw new Error('args, env, and cwd are only valid with command');\n    }\n    let parsed: URL;\n    try {\n      parsed = new URL(url);\n    } catch (err) {\n      throw new Error('url must be a valid HTTP(S) URL', { cause: err });\n    }\n    const loopback = ['localhost', '127.0.0.1', '[::1]', 'host.docker.internal'].includes(parsed.hostname);\n    if (parsed.protocol !== 'https:' && !(parsed.protocol === 'http:' && loopback)) {\n      throw new Error('url must use HTTPS (plain HTTP is allowed only for localhost and host.docker.internal)');\n    }\n    if (parsed.username || parsed.password || parsed.hash) {\n      throw new Error('url must not contain credentials or fragments; use OneCLI for authentication');\n    }\n    for (const key of parsed.searchParams.keys()) {\n      if (SECRET_QUERY_KEY_RE.test(key.replace(CAMEL_SPLIT_RE, '$1_$2'))) {\n        throw new Error(`url query parameter \"${key}\" looks like a credential; use OneCLI for authentication`);\n      }\n    }\n    const headers = parseStringRecord(input.headers, 'headers');\n    return {\n      type: 'http',\n      url,\n      ...(headers === undefined ? {} : { headers }),","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L137-L173","documentation":"new URL(url) threw while parsing the url field of an http MCP server, so parseMcpServerConfig wraps the failure as 'url must be a valid HTTP(S) URL' (the original error is attached as cause). Typical causes: missing scheme, whitespace, or a typo.","triggerScenarios":"url values like \"mcp.example.com/mcp\" (no scheme), \" https://...\" (leading space), or \"https:\\\\example.com\" passed in an http/streamable-http entry.","commonSituations":"Omitting the https:// prefix because browsers tolerate it; unescaped characters pasted from docs; trailing newline inside a shell variable.","solutions":["Include the full scheme: https://mcp.example.com/mcp","Trim whitespace/newlines around the url before passing it","Inspect err.cause in a catch block to see the underlying URL parse error"],"exampleFix":"// before\n{\"url\":\"mcp.example.com/mcp\"}\n// after\n{\"url\":\"https://mcp.example.com/mcp\"}","handlingStrategy":"validation","validationCode":"const u = entry.url?.trim(); if (!u || !/^https?:\\/\\//.test(u)) throw new UserError('url must start with http(s)://'); try { new URL(u); } catch { throw new UserError('unparseable url'); }","typeGuard":"function isParseableHttpUrl(s: unknown): s is string { if (typeof s !== 'string') return false; try { const u = new URL(s); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; } }","tryCatchPattern":"catch (err) { if (err.message.includes('valid HTTP(S) URL')) rePromptForUrl(); else throw err; }","preventionTips":["Always include the scheme","Trim whitespace before saving user-entered urls"],"tags":["mcp","url-parsing","config-validation"],"backgroundTag":"invalid-url-format","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}