Workspace

Errors

All API responses follow a consistent error format. This page lists every error code, its cause, and how to resolve it.

Error response format

{
  "success": false,
  "error": "error_code",
  "message": "Human-readable description of what went wrong"
}
FieldTypeDescription
successbooleanAlways false for errors
errorstringMachine-readable error code
messagestringHuman-readable explanation

Authentication errors

HTTPError codeCauseFix
401unauthorizedMissing or invalid API credentialsCheck X-Client-ID and X-Client-Secret headers
401token_expiredWorkspace token has expired (30-day TTL)Generate a new token via the API Access endpoint
403forbiddenValid credentials but insufficient permissionsVerify you own the workspace or have namespace access

Quota & limit errors

HTTPError codeCauseFix
403SANDBOX_LIMIT_REACHEDMaximum workspace count reached for your planDelete unused workspaces or upgrade your plan
403POOL_LIMIT_REACHEDOwned pool (total resources) exhaustedReduce resource allocations or delete workspaces
403RUNNING_POOL_REACHEDRunning pool (active resources) exhaustedStop a running workspace or upgrade your plan
403quota_exceededResource request exceeds plan per-workspace limitsReduce requested CPU, memory, or disk
503quota_unavailablePlan resolution failed temporarilyRetry the request in a few seconds

Handling quota errors

try {
  await ws.start('ws_a1b2c3d4');
} catch (err) {
  if (err.code === 'RUNNING_POOL_REACHED') {
    // List running workspaces and stop one
    const list = await ws.list({ status: 'running' });
    await ws.stop(list[0].id);
    // Retry
    await ws.start('ws_a1b2c3d4');
  }
}

Resource errors

HTTPError codeCauseFix
400bad_requestInvalid parameters (e.g., negative CPU, invalid image)Check request body against the API reference
400validation_errorRequest body fails schema validationReview required fields and types
400invalid_imageRequested image does not existUse the Images API to list available images
400invalid_resourcesCPU, memory, or disk outside allowed rangeCheck plan limits on the Pricing page

State errors

HTTPError codeCauseFix
409invalid_stateOperation not allowed in current workspace stateCheck workspace status before the operation
409must_be_runningOperation requires the workspace to be runningStart the workspace first
409must_be_stoppedOperation requires the workspace to be stoppedStop the workspace first
409must_be_pausedOperation requires the workspace to be pausedPause the workspace first

State machine

                    ┌── pause ──► Paused ──► resume ──┐
                    │                                  │
Created ──► Starting ──► Running ──► Stopping ──► Stopped
                │            │                      │
                │            └── restart ──► Starting│
                │                                    │
                └────────── destroy ◄────────────────┘

Valid transitions:

FromAllowed actions
createdstart, destroy
starting- (wait for running)
runningstop, restart, pause, destroy
stopping- (wait for stopped)
stoppedstart, destroy
pausedresume, destroy

Not found errors

HTTPError codeCauseFix
404not_foundWorkspace ID doesn't exist or was deletedVerify the workspace ID; it may have been auto-deleted (TTL/remove_on_exit)
404snapshot_not_foundReferenced snapshot doesn't existList snapshots to find valid IDs
404workload_not_foundReferenced workload doesn't existList workloads to find valid IDs

VM errors

HTTPError codeCauseFix
500server_errorUnexpected error in the VM backendRetry the request; if persistent, contact support
502vm_unavailableVM host is unreachableThe workspace may be migrating; retry after a few seconds
504vm_timeoutVM operation timed outThe workspace may be under heavy load; retry with backoff

Rate limiting

HTTPError codeCauseFix
429rate_limitedToo many requests in a short periodWait and retry with exponential backoff

Rate limits vary by plan. See the Pricing page for current limits.

Handling errors in the SDK

The SDK throws typed errors that you can catch and inspect:

import { Workspace, OblienError } from 'oblien/workspace';

try {
  const result = await ws.create({ image: 'node-20' });
} catch (err) {
  if (err instanceof OblienError) {
    console.error(`[${err.code}] ${err.message}`);
    console.error(`HTTP Status: ${err.status}`);

    switch (err.code) {
      case 'SANDBOX_LIMIT_REACHED':
        // Handle workspace limit
        break;
      case 'RUNNING_POOL_REACHED':
        // Handle running pool limit
        break;
      case 'quota_exceeded':
        // Handle resource quota
        break;
      case 'invalid_state':
        // Handle state conflict
        break;
      default:
        // Unknown error
        throw err;
    }
  }
}

Retry strategy

For transient errors (5xx, timeouts), use exponential backoff:

async function withRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (err) {
      const isRetryable = err.status >= 500 || err.code === 'rate_limited';
      if (!isRetryable || i === maxRetries - 1) throw err;

      const delay = Math.min(1000 * Math.pow(2, i), 10000);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

// Usage
const workspace = await withRetry(() => ws.create({ image: 'node-20' }));

The SDK has built-in retry logic for transient errors. You can configure it when initializing the client. See SDK Setup for details.