{"record":{"id":"1e154463a89743d2","repo":"paperclipai/paperclip","slug":"networkscope-allowlist-requires-at-least-one-val","errorCode":null,"errorMessage":"networkScope=\"allowlist\" requires at least one valid networkAllowlist hostname or HTTP(S) networkTrustedUrl.","messagePattern":"networkScope=\"allowlist\" requires at least one valid networkAllowlist hostname or HTTP\\(S\\) networkTrustedUrl\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/local-process-sandbox.ts","lineNumber":241,"sourceCode":"    \"Content-Type: application/json; charset=utf-8\",\n    `Content-Length: ${Buffer.byteLength(body)}`,\n    \"\",\n    body,\n  ].join(\"\\r\\n\");\n}\n\nasync function startNetworkAllowlistProxy(\n  allowlist: string[],\n  trustedUrls: string[],\n  socketPath: string,\n): Promise<NetworkAllowlistProxy> {\n  assertUnixSocketPathLength(socketPath);\n  const rules = [\n    ...allowlist.map(parseNetworkAllowlistEntry),\n    ...trustedUrls.map(parseTrustedNetworkUrl).filter((rule): rule is NetworkAllowlistRule => rule !== null),\n  ];\n  if (rules.length === 0) {\n    throw new Error(\n      'networkScope=\"allowlist\" requires at least one valid networkAllowlist hostname or HTTP(S) networkTrustedUrl.',\n    );\n  }\n  const server = http.createServer((request, response) => {\n    let target: URL;\n    try {\n      target = new URL(request.url ?? \"\");\n    } catch {\n      writeProxyError(response, 400, \"invalid_request_url\", \"Paperclip sandbox proxy requires an absolute request URL.\");\n      return;\n    }\n    const port = target.port || (target.protocol === \"https:\" ? \"443\" : \"80\");\n    if (target.protocol !== \"http:\") {\n      writeProxyError(response, 400, \"https_requires_connect\", \"HTTPS targets must use CONNECT through the Paperclip sandbox proxy.\");\n      return;\n    }\n    if (!isNetworkTargetAllowed(target.hostname, port, rules)) {\n      writeProxyError(response, 403, \"network_target_denied\", \"Network target denied by Paperclip sandbox policy.\");","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/local-process-sandbox.ts#L223-L259","documentation":"Thrown by startNetworkAllowlistProxy when networkScope is \"allowlist\" but the combined rules from networkAllowlist entries and HTTP(S) networkTrustedUrl entries is empty. An allowlist proxy with zero rules would block every outbound request, which is indistinguishable from \"deny\" — the library treats that as a misconfiguration and refuses to start the proxy rather than silently degrading into a full deny.","triggerScenarios":"networkScope=\"allowlist\" is set but networkAllowlist and networkTrustedUrls are both omitted, both empty arrays, or contain only entries that fail to parse. parseNetworkAllowlistEntry throws on bad entries upstream of this check, so reaching this message means the arrays produced zero rules without individual parse errors — typically because they were empty or contained only null-returning parseTrustedNetworkUrl inputs (non-HTTP(S) URLs).","commonSituations":"Config sets networkScope: \"allowlist\" but forgets to populate networkAllowlist; or networkTrustedUrls contains ftp:// or file:// URLs which parseTrustedNetworkUrl filters out (returns null), leaving zero rules. Also seen when allowlist arrays are typed but never filled because the values live under a different config key.","solutions":["Provide at least one valid entry in networkAllowlist (e.g. [\"api.openai.com\", \"example.com:443\"]) or networkTrustedUrls (e.g. [\"https://api.openai.com\"]).","If you actually want to block all network egress, switch networkScope to \"deny\" — that mode does not require any allowlist.","If using networkTrustedUrls, ensure every URL uses http: or https: — other schemes are silently filtered, which can leave the rule set empty.","Add a config-layer assertion that rejects { networkScope: \"allowlist\", networkAllowlist: [], networkTrustedUrls: [] } before sandbox spawn."],"exampleFix":"// before\nbuildLocalProcessSandboxSpawnTarget({\n  ...input,\n  options: { ...input.options, networkScope: \"allowlist\", networkAllowlist: [], networkTrustedUrls: [] },\n});\n\n// after\nbuildLocalProcessSandboxSpawnTarget({\n  ...input,\n  options: {\n    ...input.options,\n    networkScope: \"allowlist\",\n    networkAllowlist: [\"api.openai.com\"],\n    networkTrustedUrls: [\"https://api.openai.com\"],\n  },\n});","handlingStrategy":"validation","validationCode":"function assertAllowlistConfigured(opts: {\n  networkScope: string | null;\n  networkAllowlist: unknown[];\n  networkTrustedUrls: unknown[];\n}): void {\n  if (opts.networkScope !== \"allowlist\") return;\n  const valid = [...opts.networkAllowlist, ...opts.networkTrustedUrls].filter((v) => typeof v === \"string\" && v.length > 0);\n  if (valid.length === 0) {\n    throw new Error('networkScope=\"allowlist\" requires non-empty networkAllowlist or networkTrustedUrls');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await buildLocalProcessSandboxSpawnTarget(input);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('requires at least one valid networkAllowlist')) {\n    throw new ConfigError(\"Allowlist mode requires at least one hostname or HTTP(S) trusted URL.\", { cause: error });\n  }\n  throw error;\n}","preventionTips":["Co-validate { networkScope, networkAllowlist, networkTrustedUrls } at the config boundary.","If you want full deny, set networkScope=\"deny\"; do not pair \"allowlist\" with empty rules.","Reject non-HTTP(S) entries in networkTrustedUrls at the schema level — they are silently dropped and can produce an empty rule set."],"tags":["network","config-validation","sandbox","proxy"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}