hcengineering/platform · error · PlatformError

Delete allowed only for active workspaces

Error message

Delete allowed only for active workspaces

What it means

The 'delete' branch of performWorkspaceOperation refuses to delete workspaces that are not currently in 'active' mode. The two-step deletion design marks active workspaces as 'pending-deletion' and a background worker finishes the actual removal; deleting a workspace already in another lifecycle mode (e.g. archiving, archived, pending-deletion) is rejected with unknownError('Delete allowed only for active workspaces').

Source

Thrown at server/account/src/serviceOperations.ts:159

  const workspaceUuids = Array.isArray(workspaceId) ? workspaceId : [workspaceId]

  const workspaces = await getWorkspacesInfoWithStatusByIds(db, workspaceUuids)
  if (workspaces.length === 0) {
    throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, {}))
  }

  let ops = 0
  for (const workspace of workspaces) {
    const update: Partial<WorkspaceStatus> = {}
    switch (event) {
      case 'reset-attempts':
        update.processingAttempts = 0
        update.lastProcessingTime = Date.now() - processingTimeoutMs // To not wait for next step
        break
      case 'delete':
        if (workspace.status.mode !== 'active') {
          throw new PlatformError(unknownError('Delete allowed only for active workspaces'))
        }

        update.mode = 'pending-deletion'
        update.processingAttempts = 0
        update.processingProgress = 0
        update.lastProcessingTime = Date.now() - processingTimeoutMs // To not wait for next step
        break
      case 'archive':
        if (!isActiveMode(workspace.status.mode)) {
          throw new PlatformError(unknownError('Archiving allowed only for active workspaces'))
        }

        update.mode = 'archiving-pending-backup'
        update.processingAttempts = 0
        update.processingProgress = 0
        update.lastProcessingTime = Date.now() - processingTimeoutMs // To not wait for next step
        break
      case 'unarchive':

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Check workspace.status.mode before deleting; only issue event='delete' when mode === 'active'.
  2. If the workspace is already 'pending-deletion', do nothing — the background processor will complete the deletion.
  3. For archived workspaces, first run event='unarchive' to return it to active, then issue the delete.
  4. If the workspace is stuck in a transitional mode (e.g. archiving-pending-backup) due to a failed job, investigate/repair the processing state or reset processingAttempts via 'reset-attempts' before retrying.

Example fix

// before — delete regardless of mode
await client.performWorkspaceOperation(ctx, adminToken, { workspaceId: wsId, event: 'delete', params: {} })
// after — guard on mode
const [ws] = await getWorkspaceInfo(ctx, adminToken, wsId)
if (ws.status.mode !== 'active') {
  if (ws.status.mode === 'pending-deletion') return // already deleting
  await client.performWorkspaceOperation(ctx, adminToken, { workspaceId: wsId, event: 'unarchive', params: {} })
}
await client.performWorkspaceOperation(ctx, adminToken, { workspaceId: wsId, event: 'delete', params: {} })
Defensive patterns

Strategy: validation

Validate before calling

const [ws] = await getWorkspacesInfoWithStatusByIds(db, [workspaceId])
if (ws?.status.mode !== 'active') {
  throw new Error(`Cannot delete workspace in mode '${ws?.status.mode}'; only active workspaces can be deleted`)
}

Type guard

function isDeletable(ws: { status: { mode: string } } | undefined): ws is { status: { mode: 'active' } } {
  return ws !== undefined && ws.status.mode === 'active'
}

Try / catch

try {
  await client.performWorkspaceOperation(ctx, token, { workspaceId, event: 'delete', params: {} })
} catch (err) {
  if (String(err?.message).includes('Delete allowed only for active workspaces')) {
    // re-fetch status; skip if pending-deletion, unarchive first if archived
    return
  }
  throw err
}

Prevention

When it happens

Trigger: Calling performWorkspaceOperation with event='delete' (with admin or matching-workspace permission) on a workspace whose status.mode is anything other than 'active' — e.g. the workspace is archived, already pending-deletion, mid-archive/restore, or suspended.

Common situations: Double-clicking a delete button so the second request hits a workspace already in 'pending-deletion'; running a cleanup script over all workspaces including archived ones; deleting a workspace that was just archived by another admin; a workspace stuck in an intermediate mode (archiving-pending-backup) after a failed backup job.

Related errors


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