Errors and JSON
--json gives commands separate result and diagnostic channels. Keep them separate when another program reads the result:
- Standard output contains one JSON result after a successful operation.
- Standard error contains one JSON object per progress or diagnostic line.
- The process exit status identifies the failure category.
marimo-studio status --target analysis.py --json \
>status.json 2>diagnostics.jsonlEvery top-level result record has schema: 1. Each command's result shape matches the to_dict() contract of its Python record. See Python API for record fields.
Diagnostic events
JSON Lines stores one JSON object on each line, so a caller can process diagnostics as they arrive. An event has this shape:
{
"schema": 1,
"event": "diagnostic",
"command": "view create",
"severity": "info",
"code": "next-command",
"message": "uvx --with marimo-studio marimo edit analysis.py --sandbox --watch",
"details": {
"action": "edit"
}
}| Field | Type | Contract |
|---|---|---|
schema | integer | 1 |
event | string | diagnostic |
command | string or null | Parsed command path, such as view build |
severity | info, warning, or error | Diagnostic severity |
code | string | Stable machine-readable category |
message | string | Human-readable description |
exit_code | integer, optional | Process exit status associated with an error |
status | string, optional | Operation-specific state |
details | object, optional | Structured paths, revisions, hints, recovery files, and other context |
Provider or child process output that is not a valid trusted diagnostic event becomes a bounded process-output warning. Studio keeps the last 16,384 characters of that text and reports omitted character counts in details.
Progress events
Static preflight and export commands stream progress on standard error. A record has this shape:
{
"schema": 1,
"event": "progress",
"command": "view export",
"progress": {
"view": "dashboard",
"runtime": "zero-python",
"source": "marimo-export",
"event": {
"kind": "state_finished",
"completed": 3,
"total": 12,
"state": "reviewed",
"cache": {
"authored_hits": 4,
"authored_misses": 1,
"projection_hits": 2,
"projection_misses": 0
},
"elapsed_seconds": 0.42,
"message": null
}
}
}Each record includes the view, runtime, owning source, and nested event. Events owned by marimo-export keep its state counts, cache activity, elapsed time, and message unchanged. Consumers may stream, retain, or discard these records independently of the result, which stays one JSON object on standard output. A command that re-enters the notebook environment relays the same progress records, separate from process-output warnings.
Expected command failures
An expected MarimoStudioError produces one error diagnostic with its stable code, message, exit status, optional hint, optional transient: true, and diagnostic_details() fields. Its exit status follows the CLI exit status categories. Argument parsing errors use code usage-error and exit status 2. An interrupted confirmation or command uses code interrupted and exit status 130.
Python error contract
Catch MarimoStudioError for expected failures:
from marimo_studio.errors import MarimoStudioError
try:
await view.build()
except MarimoStudioError as error:
print(error.code, error.exit_code, error.transient)
print(error.diagnostic_details())Every expected error exposes:
| Attribute or method | Contract |
|---|---|
code | Stable machine-readable category |
exit_code | CLI exit status |
status_code | HTTP status used by Studio routes |
transient | Whether a fresh read or later retry may succeed |
public_hint | Repair action suitable for a public surface |
public_message() | Message safe for browser routes |
diagnostic_details() | JSON-compatible repair context |
Public error classes
| Class | Code | Exit | HTTP | Recovery contract |
|---|---|---|---|---|
MarimoStudioError | marimo-studio-error | 3 | 500 | Base class |
ConfigurationError | configuration-error | 3 | 500 | Fix saved configuration or source |
NotebookSourceError | notebook-source-error | 3 | 500 | Fix the notebook problem the message names |
ViewProjectError | view-project-error | 3 | 500 | Fix the source-located provider diagnostic and rebuild |
BindingError | binding-error | 4 | 500 | Select a valid cell or alias |
ProtocolError | protocol-error | 6 | 500 | Align installed Studio and marimo versions |
CapabilityInputError | Supplied by the caller | 2 | 400 | Fix the field named in details.field |
RuntimeTimeoutError | runtime-timeout | 3 | 504 | Fix the blocking notebook operation or increase the timeout |
AgentRequestError | Supplied by the server | 5 | Supplied by the server | Use returned details and retry classification |
DependencyError | dependency-error | 7 | 500 | Repair target Python metadata or environment preparation |
StaticExportError | static-export-error, static-delivery-preflight-failed, or a marimo-export destination_* or export_commit_failed code | 3 | 500 | Repair the source, reference, or destination named by the diagnostic |
PublicationError | zero-python-publication-error, zero-python-projection-*, or the originating marimo-export code | 3 | 409 | Repair the projected result or select another static runtime |
PublicationUnavailableError | zero-python-publication-unavailable | 3 | 409 | Open the view in Studio to prepare its notebook state, then retry |
PublicationLimitError | zero-python-state-limit | 3 | 413 | Reduce the prepared state space |
RuntimeSelectionError | runtime-unavailable | 3 | 400 | Select a runtime listed by the workspace |
RuntimeConfigTooLargeError | runtime-config-too-large | 3 | 413 | Bound projection targets or reduce notebook source |
SourceNotFoundError | source-not-found | 3 | 404 | Read the current Source catalog and select an authorized path |
SourceEncodingError | invalid-source-encoding | 3 | 400 | Save the Source document as UTF-8 |
SourceValidationError | invalid-source-content | 3 | 400 | Repair the affected source or manifest contract |
SourceTooLargeError | source-too-large | 3 | 413 | Reduce the Source document size |
SourceConflictError | source-conflict | 3 | 412 | Read the current revision, merge, and retry |
ViewNotFoundError | view-not-found | 3 | 404 | Choose an entry from available_views |
ProviderNotFoundError | provider-not-found | 3 | 404 | Install or choose an entry from available_providers |
StarterNotFoundError | starter-not-found | 3 | 404 | Choose an entry from available_starters |
ViewExistsError | view-exists | 3 | 409 | Choose another name or remove the existing view first |
InvalidViewNameError | invalid-view-name | 3 | 400 | Choose a lowercase name that starts with a letter and is not reserved |
ViewRenameError | view-rename-error | 3 | 409 | Close programs that use the view project, or resolve the named error |
ViewGenerationConflictError | view-generation-conflict | 3 | 409 | Reopen the workspace and reacquire the view |
WorkspaceGenerationConflictError | workspace-generation-conflict | 3 | 409 | Open the workspace again |
WorkspaceMutationError | workspace-mutation-incomplete | 3 | 409 | Inspect recovery and reload before retrying |
ViewDeletionError | view-deletion-error | 3 | 500 | Inspect cleanup or recovery before another mutation |
ViewInUseError | view-in-use | 3 | 409 | Rename or remove through the process in details.processes, or stop it |
LastViewError | last-view | 3 | 409 | Create the replacement view, then remove this one |
PublicationHeldError | publication-held | 3 | 409 | Release the hold named in details.owner or wait for it to expire |
Generation and incomplete-mutation errors marked transient require a fresh read before retry. A configuration-error marked transient reports a file that kept changing while Studio read it, such as a notebook during repeated saves, and the operation can be retried. AgentRequestError can also carry a server-supplied retry classification.
HTTP error responses
Studio routes encode expected errors as JSON:
{
"error": "source-conflict",
"message": "index.html changed on disk.",
"revision": "sha256:..."
}The response status equals error.status_code. The Marimo-Studio-Error response header repeats the code, and expected error responses use Cache-Control: no-store. An available hint appears as hint. A retryable failure includes transient: true.
Python runtime routes use two synchronization codes. runtime-sync-pending is transient while the editor session applies changed cells, so the browser can retry the request. runtime-sync-required is terminal for the current notebook state. Run the changed cells in the editor, then retry the preview.
Authentication failures use route-owned codes such as authentication-required, edit-access-required, missing-server-token, and invalid-server-token.