{"record":{"id":"8e30f3143e177d30","repo":"paperclipai/paperclip","slug":"exposure-port-ownership-conflict","errorCode":"exposure_port_ownership_conflict","errorMessage":"HTTPS exposure allocation denied: ${describeExposurePortConflict(conflict)}","messagePattern":"HTTPS exposure allocation denied: (.+?)","errorType":"exception","errorClass":"ExposurePortOwnershipConflictError","httpStatus":null,"severity":"error","filePath":"server/src/services/workspace-runtime.ts","lineNumber":4214,"sourceCode":"        reserved,\n        preferredAppPort,\n        claimPair: (candidate) => exposurePortPairClaims.claim(candidate),\n      });\n      claimed.push(pair);\n\n      // Complete mediation before the broker is asked for anything: persisted\n      // reservations were already folded into `reserved`, so what remains is the\n      // live host — the listener actually bound, and the Serve mapping actually\n      // published. Either one belonging to a different execution workspace is\n      // terminal, never an adoption.\n      const conflict = findExposurePairConflict({\n        pair,\n        claimant: input.claimant,\n        ledger,\n        listenerOwners: await readExposureListenerOwners([pair.appPort, pair.hmrPort]),\n        serveMappingOwners,\n      });\n      if (conflict) throw new ExposurePortOwnershipConflictError(conflict);\n\n      const result = await reserveExposure(workspaceRuntimeExposureDeps, {\n        runtimeId: input.runtimeId,\n        config: input.config,\n        appPort: pair.appPort,\n      });\n      if (result.handle) {\n        // Keep this pair's claim; the caller releases it on stop/teardown.\n        claimed.pop();\n        return { appPort: pair.appPort, hmrPort: pair.hmrPort, status: result.status, handle: result.handle };\n      }\n      if (!result.status.lastError || !retryable.has(result.status.lastError)) {\n        throw new Error(`HTTPS exposure reservation failed: ${result.status.lastError ?? \"unknown broker error\"}`);\n      }\n      reserved.add(pair.appPort);\n      reserved.add(pair.hmrPort);\n      // The preference lost its race with a conflicting/manual/quarantined\n      // mapping; drop it so the retry scans instead of re-offering the same port.","sourceCodeStart":4196,"sourceCodeEnd":4232,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/workspace-runtime.ts#L4196-L4232","documentation":"HTTPS exposure port-pair allocation was denied because a live conflict was found: a bound listener or published Serve mapping on the app/HMR port pair belongs to a different execution workspace. Cross-workspace ownership is terminal, never adopted.","triggerScenarios":"Thrown at server/src/services/workspace-runtime.ts:4157 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["The requested HTTPS exposure port conflicts with an existing allocation (details in the message). Choose a different port or release the conflicting allocation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}