hcengineering/platform · error

Missing workspace role

Error message

Missing workspace role

What it means

withOwnerAsync rejects with 401 'Missing workspace role' when the resolved loginInfo lacks a 'role' property — the account service returned login info that is not workspace-scoped (e.g. global/system login info instead of a workspace membership record).

Source

Thrown at services/payment/pod-payment/src/middleware.ts:91

const withOwnerAsync = async (req: RequestWithAuth, res: Response, next: NextFunction): Promise<void> => {
  if (req.token === undefined || req.token == null) {
    res.status(401).json({ message: 'Token error' }).end()
    return
  }
  if (req.params.workspace != null && req.token.workspace !== req.params.workspace) {
    res.status(401).json({ message: 'Workspace mismatch' }).end()
    return
  }
  if (req.token.account !== systemAccountUuid && req.token.extra?.admin !== 'true') {
    const accountClient = getAccountClient(req.headers.authorization?.split(' ')[1])
    const loginInfo = req.loginInfo ?? (await accountClient.getLoginInfoByToken())
    if (loginInfo == null) {
      res.status(403).json({ message: 'Missing auth info' }).end()
      return
    }
    if (!('role' in loginInfo)) {
      res.status(401).json({ message: 'Missing workspace role' }).end()
      return
    }
    if (loginInfo.role !== AccountRole.Owner) {
      res.status(401).json({ message: 'Workspace owners only' }).end()
      return
    }
  }

  next()
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Add the user to the target workspace with an assigned role in the account service
  2. Verify the account service version returns workspace-scoped login info including 'role' (check for API drift)
  3. Ensure the token/workspace used to fetch login info is the same workspace the route targets
  4. Check that account service data migrations completed for this workspace
Defensive patterns

Strategy: validation

Validate before calling

// verify workspace membership/role before calling owner-guarded routes
const loginInfo = await accountClient.getLoginInfoByToken()
if (loginInfo == null || !('role' in loginInfo)) {
  throw new Error('No workspace role: join the workspace before calling this API')
}

Type guard

function hasWorkspaceRole(info: unknown): info is { role: AccountRole } {
  return typeof info === 'object' && info != null && 'role' in info
}

Try / catch

try {
  const res = await callWorkspaceApi()
} catch (err) {
  if (err.response?.status === 401 && err.response.data?.message === 'Missing workspace role') {
    // direct user to join the workspace / check account service membership data
  }
}

Prevention

When it happens

Trigger: A non-admin caller whose getLoginInfoByToken() result has no 'role' field, meaning they have no workspace-scoped membership record for the token's workspace, or the account client fetched the wrong kind of login info.

Common situations: User authenticated globally but never added to the workspace; account service version returning a shape without role (API change); requesting login info for the wrong workspace context; data migration leaving workspace memberships incomplete.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/ff7b5fb64ec7ac98. Report an issue: GitHub.