hcengineering/platform · error

Invalid workspace

Error message

Invalid workspace

What it means

The API returns 401 'Invalid workspace' when the token decodes fine, the workspace-info cache misses, and the account service's getLoginWithWorkspaceInfo() contains no membership entry for the workspace named in the URL, while the caller is not an admin. The service only lets non-admin users fetch backups of workspaces they belong to.

Source

Thrown at services/backup/backup-api-pod/src/server.ts:190

        res.status(401).end('Unauthorized')
        return
      }
      workspaceId = decoded.workspace
      isAdmin = decoded.extra?.admin === 'true'
    } catch (err: any) {
      res.status(401).end('Unauthorized')
      return
    }
    let wsInfo: WorkspaceIds | undefined = wsInfoCache.get(workspaceId)
    const accountClient = getClient(config.AccountsUrl, token)

    if (wsInfo === undefined) {
      try {
        const info = await accountClient.getLoginWithWorkspaceInfo()
        const winfo = info.workspaces[workspaceId]
        if (!isAdmin) {
          if (winfo === undefined) {
            res.status(401).end('Invalid workspace')
            return
          } else {
            if (winfo.role !== AccountRole.Owner) {
              res.status(401).end('Not an owner of workspace')
              return
            }
          }
        }
        const wssInfo = await accountClient.getWorkspaceInfo()
        wsInfo = {
          url: wssInfo.url,
          dataId: wssInfo.dataId,
          uuid: workspaceId
        }
        wsInfoCache.set(workspaceId, wsInfo)
      } catch (err: any) {
        res.status(401).end('Invalid workspace')
        return

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Verify the workspace UUID in the URL matches a workspace the token's account belongs to
  2. Ask a workspace owner to add your account to the workspace
  3. Use a token belonging to a member of the target workspace
  4. If you should be an admin, ensure the token's extra payload includes admin='true'

Example fix

// before
const res = await fetch(`/api/backup/${wrongUuid}/backup.json.gz`)
// after
const res = await fetch(`/api/backup/${myWorkspaceUuid}/backup.json.gz`, { headers: { Authorization: `Bearer ${token}` } })
Defensive patterns

Strategy: validation

Validate before calling

const info = await accountClient.getLoginWithWorkspaceInfo()
if (!isAdmin && info.workspaces[workspaceUuid] === undefined) {
  throw new Error(`Not a member of workspace ${workspaceUuid}`)
}

Type guard

function canAccessWorkspace(info: { workspaces: Record<string, unknown> }, wsId: string, isAdmin: boolean): boolean {
  return isAdmin || info.workspaces[wsId] !== undefined
}

Try / catch

try { ... } catch (e) {
  if (e.message === 'Invalid workspace') console.error('Check workspace UUID and membership')
  throw e
}

Prevention

When it happens

Trigger: GET /api/backup/<workspaceUuid>/<file> where workspaceUuid in the path is not a key of info.workspaces returned by the account client for that token, and decoded.extra?.admin !== 'true'.

Common situations: Typing or pasting the wrong workspace UUID in the URL, using a personal token for a workspace the user was removed from, or hitting the backup API before being added to the workspace.

Related errors


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