Skip to main content

HTTP Error Status Codes Reference

This document describes the HTTP status codes used by the two main IdeGYM services: the Orchestrator (Kubernetes orchestration service) and the IdeGYM Server (in-container tool/reward API).


Reading these codes from the Python client​

The client turns each of these into a typed exception rather than a bare RuntimeError: IdeGYMNotFoundError for 404 and 410, IdeGYMBusyError for 429 and 503, IdeGYMTimeoutError for 408 and 504, and so on. The status and body are attributes on the exception. A request that got no status at all raises IdeGYMTimeoutError for a client-side deadline, or IdeGYMConnectionError when the connection failed, both with status_code set to None. The full mapping is in Client Library — Error Handling.

The IdeGYM Server codes further down reach the client through the orchestrator's forwarding endpoint, and are raised as IdeGYMSandboxError with the server's own status and body rather than through the mapping — a 404 from a live server's file API is not a missing sandbox. The forwarding endpoint's own 410 (pod unreachable) and 499 (cancelled) keep their usual types.

A failure reported through an async operation's result.status_code is mapped the same way, with one exception: start_server treats a 429 from the background operation as "quota full, try again", and resubmits every retry_delay_in_seconds until its start wait runs out. Only then does it raise IdeGYMBusyError, carrying the last 429 and its body — so except IdeGYMBusyError is where an exhausted quota lands, not IdeGYMTimeoutError. A failed client registration on entering IdeGYMClient raises the typed error for its status in the same way.


Async Operation Pattern (Orchestrator)​

Most orchestrator endpoints that trigger long-running work are asynchronous: the HTTP response is returned immediately with 202 Accepted and an operation_id, while the actual work runs in the background. The final outcome is retrieved by polling:

GET /api/operations/status/{operation_id}

For these endpoints, HTTP-level error codes (4xx/5xx) only cover request validation failures that are detected synchronously (e.g. client not found, resource limit exceeded). Any error that occurs during background execution is reported via the operation status response fields:

FieldTypeDescription
statusAsyncOperationStatusSCHEDULED, IN_PROGRESS, SUCCEEDED, FAILED, CANCELLED, FINISHED_BY_WATCHER
result.status_codeint | nullHTTP-equivalent error code when status == FAILED or CANCELLED
result.bodystr | nullError detail message

Orchestrator Endpoints​

Diagnostics​

MethodPathSuccessError
GET/health200 OK—
GET/metrics200 OK (OpenMetrics)—

Clients — POST /api/clients​

Register or re-register a client. If node provisioning is needed, an async operation is started.

ScenarioHTTP StatusDetails
Client registered, no nodes to spin up200 OKRegisteredClientResponse, no operation_id
Client registered, nodes being provisioned202 AcceptedRegisteredClientResponse with operation_id for polling
Internal error500 Internal Server ErrorJSON detail with error description

On success, status == SUCCEEDED and result contains the RegisteredClientResponse body.

Async operation non-success outcomes (polled via GET /api/operations/status/{operation_id}):

Scenarioresult.status_codestatus
Node provisioning failed500FAILED
Operation cancelled499CANCELLED

Clients — POST /api/clients/heartbeat​

Update client availability status.

ScenarioHTTP StatusDetails
Success200 OKRegisteredClientResponse
Client not found404 Not Founddetail: "Client with ID {id} not found"
Internal error500 Internal Server ErrorJSON detail

Clients — DELETE /api/clients​

Stop a client and all its associated servers. Always async.

ScenarioHTTP StatusDetails
Accepted202 AcceptedStopClientResponse with operation_id for polling
Client not found404 Not Founddetail: "Client with ID {id} not found"
Internal error500 Internal Server ErrorJSON detail

On success, status == SUCCEEDED and result contains the RegisteredClientResponse body.

Async operation non-success outcomes:

Scenarioresult.status_codestatus
One or more servers/nodes failed to stop500FAILED
Operation cancelled499CANCELLED

Clients — POST /api/clients/finish​

Mark a client and its servers as finished (available for reuse by other clients).

ScenarioHTTP StatusDetails
Success200 OKRegisteredClientResponse
Client not found404 Not Founddetail: "Client with ID {id} not found"
Internal error500 Internal Server ErrorJSON detail

Servers — POST /api/idegym-servers​

Start a new IdeGYM server (or reuse a finished one). Always async.

ScenarioHTTP StatusDetails
Accepted202 AcceptedStartServerResponse with operation_id
Client not found404 Not Founddetail: "Client with ID {id} not found"
Resource limit exceeded429 Too Many Requestsdetail: "Resource limit exceeded..."
FIFO queue blocked429 Too Many Requestsdetail: "Server reuse blocked due to pending START_SERVER operations..."
Internal error500 Internal Server ErrorJSON detail

On success, status == SUCCEEDED and result contains the StartServerResponse body.

Async operation non-success outcomes:

Scenarioresult.status_codestatus
Server failed to start (K8s/timeout error)500FAILED
HTTP validation error during startupvaries (4xx/5xx)FAILED
Operation cancelled499CANCELLED

Servers — DELETE /api/idegym-servers​

Stop an IdeGYM server and clean up its Kubernetes resources. Always async.

ScenarioHTTP StatusDetails
Accepted202 AcceptedServerActionResponse with operation_id
Client not found404 Not Founddetail: "Client with ID {id} not found"
Server not found404 Not Founddetail: "IdeGYM server with ID {id} not found"
Server not active410 Gonedetail: "IdeGYM server with ID {id} is not available (status: ...)"
Internal error500 Internal Server ErrorJSON detail

On success, status == SUCCEEDED and result contains the ServerActionResponse body.

Async operation non-success outcomes:

Scenarioresult.status_codestatus
Kubernetes cleanup failed500FAILED
Operation cancelled499CANCELLED

Servers — POST /api/idegym-servers/finish​

Mark a server as finished (not stopped — pod remains running, available for reuse).

ScenarioHTTP StatusDetails
Success200 OKServerActionResponse
Client not found404 Not Founddetail: "Client with ID {id} not found"
Server not found404 Not Founddetail: "IdeGYM server with ID {id} not found"
Server not active410 Gonedetail: "IdeGYM server with ID {id} is not available (status: ...)"
Internal error500 Internal Server ErrorJSON detail

Servers — GET /api/idegym-servers​

List the servers a client owns. Synchronous.

ScenarioHTTP StatusDetails
Success200 OKListServersResponse; an empty list when the client owns nothing
Client not found404 Not Founddetail: "Client with ID {id} not found"
Internal error500 Internal Server ErrorJSON detail

Query parameters: client_id (required) and include_terminal (default false).

Servers — POST /api/idegym-servers/keepalive​

Hold a server against the watcher's inactivity reaper for minutes from now. Synchronous.

ScenarioHTTP StatusDetails
Success200 OKKeepaliveServerResponse with the window actually in effect
minutes out of range (0 < minutes ≤ 1440)422 Unprocessable EntityPydantic validation error
Client not found404 Not Founddetail: "Client with ID {id} not found"
Server not found or owned by another client404 Not Found
Server already in a terminal state410 Gonedetail: "... is KILLED and cannot be held alive"
Internal error500 Internal Server ErrorJSON detail

A server that has already reached a terminal state cannot be held: the hold is refused with 410 Gone. That is the race the endpoint exists to lose gracefully — a keepalive loop can be overtaken by the reaper — so treat a 410 here as "the sandbox is gone, start a new one" rather than as an error to retry. The window only ever extends, so a shorter request against a longer existing hold returns the longer one.

Servers — GET /api/idegym-servers/{server_id}/status​

Report a server's availability, pod phase and idle time. Synchronous, and read-only: it does not update the server's activity timestamp, so polling it will not keep a server alive.

ScenarioHTTP StatusDetails
Success200 OKServerStatusResponse — including for a finished, stopped or crashed server
Client not found404 Not Founddetail: "Client with ID {id} not found"
Server not found404 Not Founddetail: "IdeGYM server with ID {id} not found"
Server owned by another client404 Not Founddetail: "... is not associated with client ID {id}"
Internal error500 Internal Server ErrorJSON detail

Unlike the other server endpoints this one does not return 410 Gone for an inactive server — reporting that state is the reason it exists.

The pod fields are best-effort. For a server in a terminal state Kubernetes is not asked at all, and if the pod lookup fails (an API timeout, missing RBAC, a deleted namespace) the orchestrator logs a warning and still answers 200 OK from the database record. In both cases pod_phase and pod_ready are null; availability and details are always served.

Servers — POST /api/idegym-servers/restart​

Restart an IdeGYM server's pods. Always async.

ScenarioHTTP StatusDetails
Accepted202 AcceptedServerActionResponse with operation_id
Client not found404 Not Found
Server not found / not active404 Not Found / 410 Gone
Internal error500 Internal Server ErrorJSON detail

On success, status == SUCCEEDED and result contains the ServerActionResponse body.

Async operation non-success outcomes:

Scenarioresult.status_codestatus
Restart failed500FAILED
Operation cancelled499CANCELLED

Forwarding — GET|POST|PUT|DELETE|PATCH /api/forward/{client_id}/{server_id}/{path}​

Forward an HTTP request to an IdeGYM server's tool/reward API. Always async.

ScenarioHTTP StatusDetails
Accepted202 AcceptedForwardRequestResponse with async_operation_id
Client not found404 Not Found
Server not found / not active404 Not Found / 410 Gone
Internal error500 Internal Server ErrorJSON detail

Unlike other async endpoints, result always carries the upstream HTTP status code regardless of outcome.

Async operation outcomes:

Scenarioresult.status_codestatus
Request forwarded successfullyupstream status (2xx/3xx)SUCCEEDED
Upstream returned 4xx errorupstream 4xxFAILED
Upstream returned 5xx errorupstream 5xxFAILED
Cannot connect to server410FAILED
Client disconnected mid-stream499CANCELLED
Internal forwarding error500FAILED

The Python client raises the upstream 4xx and 5xx rows as IdeGYMSandboxError, carrying the upstream status and body, and the last three rows through the usual mapping: 410 as IdeGYMNotFoundError, 499 as IdeGYMCancelledError, and the orchestrator's own 500 as IdeGYMServerError. The synchronous 404/410 above, returned before anything is forwarded, are the orchestrator's too.

Forwarding — WS /api/ws-forward/{client_id}/{server_id}/ws​

Upgrade an HTTP connection to a WebSocket and relay it to the IdeGYM server.

ScenarioHTTP/WS StatusDetails
Upgrade accepted101 Switching ProtocolsBidirectional relay established
Client not found404 Not FoundHTTP response before upgrade
Server not found / not active404 Not Found / 410 GoneHTTP response before upgrade
Internal error (post-upgrade)WS close 1011 Internal ErrorWebSocket close frame

Operations — GET /api/operations/status/{operation_id}​

Poll the status of an async operation.

ScenarioHTTP StatusDetails
Success200 OKAsyncOperationStatusResponse
Operation not found404 Not Founddetail: "Operation with ID {id} not found"
Internal error500 Internal Server ErrorJSON detail

Build Images — POST /api/build-push-images​

Start Kaniko image build jobs from a YAML spec.

ScenarioHTTP StatusDetails
Jobs started200 OKBuildFromYamlResponse with job_names: list[str]
Internal error500 Internal Server ErrorJSON detail

Note: This endpoint does not use the async operation pattern. Job status must be polled separately via GET /api/jobs/status/{job_name}.

Build Images — GET /api/jobs/status/{job_name}​

Get Kaniko job status by job name.

ScenarioHTTP StatusDetails
Success200 OKJobStatusResponse
Job not found404 Not Founddetail: "Kaniko job with name {name} not found"
Internal error500 Internal Server ErrorJSON detail

IdeGYM Server Endpoints​

The IdeGYM Server runs inside each container and provides synchronous tool and reward APIs. All responses are synchronous — no async operation polling is required.

Global Exception Handlers​

These handlers apply to all server endpoints:

Exception TypeHTTP Status
FileNotFoundError404 Not Found
PermissionError403 Forbidden
OSError (other)400 Bad Request
BashExecutorRequestError — unknown user, missing cwd, no way to switch user400 Bad Request
BashCommandExecutionTimeoutError500 Internal Server Error
Exception (catch-all)500 Internal Server Error

All error responses include a JSON body: {"timestamp": "...", "message": "...", "traceback": "..."}.

Actuator​

MethodPathSuccessError
GET/api/health200 OK (empty body)—
GET/api/metrics200 OK (OpenMetrics)—
GET/api/log200 OK (streaming binary)404 if log file missing
GET/api302 Found → /api/health—
POST/api/shutdown202 Accepted—

Tools — Bash​

MethodPathSuccessError
POST/api/tools/bash200 OK400 on a bad cwd or user; 422 on an invalid env; 500 on timeout; bash failure is in exit_code

Response: {stdout, stderr, exit_code}. A non-zero exit_code does not produce an HTTP error — it is encoded in the response body.

A cwd that is not a directory, a user that does not exist, and a user the server cannot switch to (it is neither root nor allowed passwordless sudo) are rejected with 400 Bad Request before anything runs, with the standard error body above. The same holds for every endpoint that runs a command through the bash executor, not only this one.

Tools — File Operations​

MethodPathSuccessError
POST/api/tools/file/create200 OK {status: "SUCCESS"}400/403/404/500 via global handlers
POST/api/tools/file/edit200 OK {status: "SUCCESS"}400/403/404/500 via global handlers
POST/api/tools/file/patch200 OK {status: "SUCCESS"}400/403/404/500 via global handlers

Filesystem API​

Path validation is performed by the valid_workspace_path dependency for all /api/fs/* endpoints before the handler runs:

Validation FailureHTTP Status
Invalid path (resolution error)400 Bad Request
Path escapes workspace root403 Forbidden

GET /api/fs/ls/{path} — List directory​

ScenarioHTTP StatusDetails
Success200 OKJSON array of entry names
Path not found404 Not Founddetail: "Path not found: {path}"
Path is not a directory400 Bad Requestdetail: "Path is not a directory: {path}"
Path escapes workspace403 Forbidden—
Path resolution error400 Bad Requesterror string

GET /api/fs/cat/{path} — Read file​

ScenarioHTTP StatusDetails
Success200 OKStreaming binary content
Path not found404 Not Founddetail: "Path not found: {path}"
Path is not a file400 Bad Requestdetail: "Path is not a file: {path}"
Path escapes workspace403 Forbidden—

PUT /api/fs/touch/{path} — Create or touch file​

ScenarioHTTP StatusDetails
File created201 Createdempty body
File already exists200 OKempty body
Parent directory not found404 Not Found—
Path escapes workspace403 Forbidden—
File creation failed500 Internal Server Error—

PUT /api/fs/mkdir/{path} — Create directory​

ScenarioHTTP StatusDetails
Directory created201 Createdempty body
Directory already exists200 OKempty body
Path exists but is not a directory400 Bad Request—
Parent directory not found (no parents flag)404 Not Found—
Path escapes workspace403 Forbidden—

DELETE /api/fs/rm/{path} — Delete file or directory​

ScenarioHTTP StatusDetails
Deleted successfully200 OKempty body
Path not found404 Not Found—
Attempt to delete workspace root403 Forbidden—
Directory not empty and recursive=false400 Bad Requestdetail: "Directory is not empty. Use recursive=true to delete it."
Path escapes workspace403 Forbidden—

Rewards​

Reward endpoints always return 200 OK. Success or failure of the underlying operation is encoded in the status field of the response body (SUCCESS, FAILURE, IN_PROGRESS). HTTP errors are only raised for infrastructure failures (timeout, OS errors, exceptions) via global handlers.

MethodPathSuccessError
POST/api/rewards/setup200 OK {status, output}500 on timeout/exception
POST/api/rewards/compilation200 OK {status, output}500 on timeout/exception
POST/api/rewards/test200 OK {status, scores}500 on timeout/exception

Project​

MethodPathSuccessError
POST/api/project/reset200 OK {status, output}500 on timeout/exception

Note: If no archive is configured for the project, the response is 200 OK with status: "FAILURE" and output: "Can not reset project without an archive!". This is a server misconfiguration and should ideally return a 5xx status, but the current contract preserves 200 OK to keep the response format consistent with the reward endpoints.


Status Code Reference Summary​

CodeMeaningWhere Used
200 OKSuccess (synchronous)All synchronous successful responses
201 CreatedResource createdPUT /api/fs/touch, PUT /api/fs/mkdir
202 AcceptedAccepted for async processingAll async orchestrator endpoints; POST /api/shutdown
302 FoundRedirectGET /api
307 Temporary RedirectRedirect/dashboard → /dashboard/servers
400 Bad RequestInvalid request or bad pathPath not a directory/file, bad workspace path, non-empty dir delete, bad bash cwd/user
403 ForbiddenAccess deniedPath escapes workspace, PermissionError, deleting workspace root
404 Not FoundResource missingClient/server/operation/job not found, path not found
410 GoneServer no longer availableServer status is terminal (stopped/crashed/killed/etc.)
429 Too Many RequestsResource limit exceededResource quota full, FIFO queue blocked
499 (non-standard)Client closed connectionStored in result.status_code of cancelled async operations
500 Internal Server ErrorUnexpected server errorUnhandled exceptions, K8s errors, bash timeout