{"record":{"id":"c9c4e79b5fd1d40c","repo":"paperclipai/paperclip","slug":"broker-protected-ports-contains-a-non-numeric-entr","errorCode":null,"errorMessage":"BROKER_PROTECTED_PORTS contains a non-numeric entry: ${JSON.stringify(token)}","messagePattern":"BROKER_PROTECTED_PORTS contains a non-numeric entry: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/tailscale-https-broker/src/port-policy.ts","lineNumber":36,"sourceCode":"}\n\n/**\n * Parse `BROKER_PROTECTED_PORTS` (comma/space separated) into a sorted, deduped\n * set of operator-protected ports (PAP-17285).\n *\n * Fails closed: a malformed list throws so the broker refuses to start rather\n * than silently protecting nothing. Protecting a port the broker cannot mutate\n * anyway is harmless, so no range restriction is applied — but `443` is rejected\n * because the primary route has its own stronger, non-optional invariant and\n * listing it here would imply it were opt-in.\n */\nexport function parseProtectedPorts(raw: string | undefined): number[] {\n  if (raw === undefined) return [];\n  const tokens = raw.split(/[,\\s]+/).filter((token) => token.length > 0);\n  const ports = new Set<number>();\n  for (const token of tokens) {\n    if (!/^[0-9]{1,5}$/.test(token)) {\n      throw new Error(`BROKER_PROTECTED_PORTS contains a non-numeric entry: ${JSON.stringify(token)}`);\n    }\n    const port = Number(token);\n    if (port < 1 || port > 65535) {\n      throw new Error(`BROKER_PROTECTED_PORTS contains an out-of-range port: ${token}`);\n    }\n    if (port === 443) {\n      throw new Error(\"BROKER_PROTECTED_PORTS must not list 443; the primary route is always protected\");\n    }\n    ports.add(port);\n  }\n  return [...ports].sort((a, b) => a - b);\n}\n","sourceCodeStart":18,"sourceCodeEnd":49,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/packages/tailscale-https-broker/src/port-policy.ts#L18-L49","documentation":"parseProtectedPorts guard in loadHostConfig startup: a token in the comma/space-separated BROKER_PROTECTED_PORTS list doesn't match the digits-only pattern. Fails closed — the broker refuses to start rather than silently protecting nothing after skipping the bad entry.","triggerScenarios":"Thrown at packages/tailscale-https-broker/src/port-policy.ts:36 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Remove or correct the non-numeric entry in BROKER_PROTECTED_PORTS; use a comma-separated list of port numbers."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"120ae5428fa29bee300bcf806491cd4d965fbb7c","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}