{"record":{"id":"c6e317cdcdaaf4ec","repo":"hcengineering/platform","slug":"missing-workspace-url-in-login-info","errorCode":null,"errorMessage":"Missing workspace url in login info","messagePattern":"Missing workspace url in login info","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"services/payment/pod-payment/src/server.ts","lineNumber":222,"sourceCode":"        'create-subscription',\n        async (ctx) => {\n          const workspaceUuid = req.token?.workspace\n          const accountUuid = req.token?.account\n          const request = req.body as SubscribeRequest\n          const loginInfo = req.loginInfo as WorkspaceLoginInfo\n\n          if (accountUuid === undefined) {\n            res.status(401).json({ error: 'Missing account in token' })\n            return\n          }\n\n          if (workspaceUuid === undefined) {\n            res.status(401).json({ error: 'Missing workspace in token' })\n            return\n          }\n\n          if (loginInfo?.workspaceUrl === undefined) {\n            res.status(401).json({ error: 'Missing workspace url in login info' })\n            return\n          }\n\n          if (request.type === undefined || request.plan === undefined) {\n            res.status(400).json({ error: 'Missing required fields: type, plan' })\n            return\n          }\n\n          let createSubResponse: CheckoutResponse\n\n          try {\n            createSubResponse = await provider.createSubscription(\n              ctx,\n              request,\n              workspaceUuid,\n              loginInfo.workspaceUrl,\n              accountUuid\n            )","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/payment/pod-payment/src/server.ts#L204-L240","documentation":"The payment service rejects a subscribe request (HTTP 401) when the workspace login info fetched via the withLoginInfo middleware does not contain a workspaceUrl. This service needs the workspace's URL to hand to the payment provider when creating a checkout/subscription, so a login record without it is treated as an authentication-context failure. It indicates the workspace's stored login data is incomplete rather than a malformed request.","triggerScenarios":"POST /api/v1/subscriptions/:workspace/subscribe where the authenticated token carries a valid workspaceUuid but the WorkspaceLoginInfo record for that workspace (loaded by withLoginInfo) has workspaceUrl === undefined or the loginInfo object itself is missing.","commonSituations":"Workspaces created before the workspaceUrl field was introduced (schema migration gap); a workspace whose login/registration flow never recorded its public URL; test or seeded workspaces built directly in the DB without login info; account-service outages causing withLoginInfo to return an empty record.","solutions":["Backfill the workspace's login record so workspaceUrl is set (or fix the account service to always populate it at workspace creation).","Check that withLoginInfo is resolving login info for the correct workspace UUID from the token; verify no middleware ordering issue yields undefined loginInfo.","If this happens after a version change, run the migration that adds workspace_url to existing workspaces.","As a client, re-register/update the workspace via the account API to regenerate complete login info, then retry the subscribe call."],"exampleFix":"// before (workspace created without login info)\nPOST /api/v1/subscriptions/ws-123/subscribe\n// -> 401 { error: 'Missing workspace url in login info' }\n\n// after (backfill login info first)\nawait accountClient.upsertWorkspaceLoginInfo({ workspaceUuid: 'ws-123', workspaceUrl: 'https://ws-123.example.com' })\nPOST /api/v1/subscriptions/ws-123/subscribe // -> 200 CheckoutResponse","handlingStrategy":"validation","validationCode":"const info = await accountClient.getWorkspaceLoginInfo(workspaceUuid)\nif (info?.workspaceUrl === undefined || info.workspaceUrl === '') {\n  throw new Error(`Workspace ${workspaceUuid} has no workspaceUrl; backfill login info before subscribing`)\n}","typeGuard":"function hasWorkspaceUrl(info: WorkspaceLoginInfo | undefined | null): info is WorkspaceLoginInfo & { workspaceUrl: string } {\n  return typeof info?.workspaceUrl === 'string' && info.workspaceUrl.length > 0\n}","tryCatchPattern":"const res = await fetch(url, opts)\nif (res.status === 401) {\n  const body = await res.json()\n  if (body.error === 'Missing workspace url in login info') {\n    // backfill login info, then retry once\n  }\n}","preventionTips":["Always create workspaces through the account API that populates login info atomically.","Add a DB check/constraint or startup audit for workspaces missing workspace_url.","Run schema migrations before deploying code that depends on new login-info fields.","In integration tests, assert every fixture workspace has a workspaceUrl."],"tags":["http-401","payment","missing-field","middleware"],"backgroundTag":"missing-workspace-url-in-login-info","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}