hcengineering/platform · error · PlatformError

platform.status.WorkspaceNotFound

platform.status.WorkspaceNotFound

Error message

WorkspaceNotFound

What it means

createAccessLink looks up the workspace UUID from the token via db.workspace.findOne. If the workspace row does not exist, it throws WorkspaceNotFound. The token is structurally valid but references a deleted or foreign workspace.

Source

Thrown at server/account/src/operations.ts:775

    expiration?: number
    personalized?: boolean
  }
): Promise<string> {
  const { role, firstName, lastName, navigateUrl, spaces, notBefore, expiration, personalized = true } = params
  const { account, workspace: workspaceUuid, extra } = decodeTokenVerbose(ctx, token)

  const currentAccount = await db.account.findOne({ uuid: account })
  if (currentAccount == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account }))
  }

  if (workspaceUuid == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
  }

  const workspace = await db.workspace.findOne({ uuid: workspaceUuid })
  if (workspace == null) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid }))
  }

  let extraObj: Record<string, string> | undefined

  if (params.extra != null) {
    try {
      extraObj = JSON.parse(params.extra)
    } catch (e) {
      ctx.error("Invalid extra parameter, couldn't parse JSON", { extra: params.extra })
      throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
    }
  }

  const RECENT_PAST_MS = 1577836800000 // January 1, 2020 in milliseconds

  if (notBefore !== undefined && notBefore > RECENT_PAST_MS) {
    ctx.error('Not before appears to be in milliseconds instead of seconds', { nbf: notBefore })
    throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Re-authenticate to get a token bound to an existing workspace.
  2. Verify the workspace exists: db.workspace.findOne({ uuid: <workspace from token> }).
  3. Check you are connecting to the same environment the token was issued for.
  4. If the workspace was deleted intentionally, direct users to a remaining workspace before generating access links.

Example fix

// before: token referencing deleted workspace
const link = await createAccessLink(staleToken, params)
// after: pick a workspace the account still belongs to
const token = await loginForWorkspace(email, password, existingWorkspaceUuid)
const link = await createAccessLink(token, params)
Defensive patterns

Strategy: try-catch

Validate before calling

const wsUuid = decodeJwt(token)?.workspace
if (wsUuid != null && !(await workspaceExists(wsUuid))) {
  throw new Error('Workspace from token no longer exists; re-authenticate')
}

Type guard

function isWorkspaceRef(w: unknown): w is { uuid: string } {
  return typeof w === 'object' && w !== null && typeof (w as any).uuid === 'string'
}

Try / catch

try {
  const link = await createAccessLink(token, params)
} catch (err) {
  if (isPlatformError(err, platform.status.WorkspaceNotFound)) {
    const token = await loginForWorkspace(email, password, pickExistingWorkspace())
    // retry once with fresh token
  } else throw err
}

Prevention

When it happens

Trigger: Calling createAccessLink with a token whose workspace UUID was deleted, or that belongs to a different database/environment than the one being queried.

Common situations: Workspace removed (or archived) while users still hold tokens for it; restoring one environment's DB while using another's tokens; hard-coded workspace UUIDs in scripts pointing at the wrong instance.

Related errors


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