CLI Reference
Installing the barca package (pip install barca or uv add barca) puts the barca command
in the environment’s bin/. The output on this page was captured from a terminal running the
current release, with long paths shortened. barca <command> --help and barca docs describe
the version you have installed.
Use barca --help to discover commands by purpose, barca <command> --help for concise
usage, and barca <command> --examples for the complete runnable example collection.
For detailed behavior, load one topic with barca docs <topic>; barca docs --json lists
available topics for scripts and agents. Help and examples never load a project or run work.
Command shape
Section titled “Command shape”The target comes first, then the files:
barca get <asset> [files] # assets: served from cache when nothing changedbarca run <task> [files] # tasks: the task always runs-
getis for assets and sensors,runis for tasks. Both serve unchanged upstream assets from cache. -
Using the wrong one exits 2 and names the other, for example
'validate' is a task — use `barca run` instead. -
A file before the target exits 2 and prints the corrected command:
$ barca run pipeline.py notifyerror: the target comes before the filesbarca run notify pipeline.py
barca list, barca status, --dry-run and barca sql show what barca sees without running
anything.
The CLI contract lists every command, flag, environment variable, exit code and JSON output schema, marked stable or experimental, with the policy for changing them.
Commands
Section titled “Commands”barca get [target[,target...]] [file.py|dir/ ...] [--refresh a,b [--no-cascade] | --refresh-all] Get assets — cache-aware; prints their artifact pathsbarca run <task[,task...]> [file.py|dir/ ...] [--refresh a,b [--no-cascade] | --refresh-all] Run task(s) (always re-run)barca plan [file.py|dir/ ...] Emit the execution plan as JSON (experimental)barca doctor [file.py|dir/ ...] [--json] [--server [URL] | --local] Diagnose project prerequisites (and a server) without running stepsbarca logs [run_id] [--step NAME] [--stream S] [-f] [--json] A run's recorded step stdout and stderr (default: the most recent run)barca history [RUN_ID] [--status S] [--target NAME] [--triggered-by T] [--since WHEN] [-l N | --all] [--json|--pretty] Show recent runs, or one run's steps and errorsbarca stats <target> [file.py|dir/ ...] Show timing/cache stats for an assetbarca gc [--dry-run] [--max-size SIZE] [--json] Evict cached results to keep .barca/ under its size limit (experimental)barca serve [file.py|dir/ ...] [--port N] [--host IP] [--watch] [--no-schedule] [--timezone TZ] [--read-only] [--drain-timeout SECONDS] [--max-runs N] [--capacity N] [--run-timeout SECONDS] Run the HTTP API serverbarca queue [pause|resume] [--server [URL]] [--json] A server's run queue; pause or resume it (experimental)barca list [file.py|dir/ ...] [-l N | --all] [--json] List discovered definitions and their depsbarca status [target[,target...]] [file.py|dir/ ...] [--json] [--sample N] [--problems] [--state S,S] Cache state, last run and artifact shape per nodebarca sql "<query>" [file.py|dir/ ...] [--json] [-l N | --all] Query cached results with DuckDB (experimental) (install: uv add 'barca[sql]')barca state backups [--json] List the metadata backups, local and in the storebarca state restore [--from N|NAME|PATH] Put a metadata backup back (the current DB is kept aside)barca state reset [--yes] Start the metadata DB over (the old one is moved aside)barca docs [topic] [--all] [--json] Built-in manualbarca version Print versionbarca --help Show helpget, run, status, list, history and stats also take --server [<url>], which runs
the command on a barca serve and prints it here as the local command would, and --local,
which ignores a configured server (experimental; Server mode,
barca docs server-mode). sql takes both too: a server resolves its views, and the query
always runs here. doctor takes both too: --server checks that server beside this
machine (a configured one is checked without it), --local checks this machine only.
barca pipeline.py is read as barca get pipeline.py. It is kept for compatibility; write
barca get.
Files are optional on every command. Without them barca reads every .py file under the project
root (the nearest directory holding barca.toml, else the current one) that imports barca.
Files or directories narrow it; a directory given first needs a trailing / or must be ..
Node ids are relative to the root. See Discovery.
Output format
Section titled “Output format”get, run, list, status, sql, history and stats choose what to print on stdout by
one rule, first match wins:
- A flag:
--jsonforces JSON;--prettyforces human output (tables, summaries).getandrunalso keep-o json|pretty, older spellings that cannot be combined with--json/--pretty.get -o path|valuechoose whatgetreturns (see get);run -o valueprints only the task’s value. BARCA_OUTPUT=jsonorBARCA_OUTPUT=prettyin the environment. Any other value is a usage error (exit code 2).- The terminal: stdout is a TTY → human output; a pipe, file or subprocess → JSON.
barca list pipeline.py # a table in your terminalbarca list pipeline.py | cat # JSON: {"nodes": [...], "total", "truncated", "root"}barca list pipeline.py --json # JSON even in a terminalbarca history --pretty # a table even when pipedBARCA_OUTPUT=json barca get summary pipeline.pyget and run print their JSON as one line. list, status, sql, stats, history and
plan print one indented JSON document over several lines, so parse all of stdout, not its
last line.
plan always prints JSON and docs always prints markdown (docs --json for JSON). Progress and
errors always go to stderr; the progress bar (the only ANSI output) draws only when stderr is a
terminal, and --agent replaces it with plain progress lines.
A library warning that a step’s process repeats is printed once per run and then counted
([barca] 79 more: ...); see barca docs agents, “Repeated warnings”.
plan, get, run and --dry-run also report plan-time warnings: one [barca] warning: ...
line each on stderr, and a warnings array in their JSON output ([] when there are none).
The one warning so far is unused_input: a step declares an input its function never uses,
which is still loaded and still part of the cache key. Warnings do not change the exit code
(barca docs assets, “Unused inputs”). A query receiver shadowed by a match capture
or nested function/class definition is treated conservatively, so dynamic SQL access does
not cause a false unused-input warning.
Compute one or more assets and print where their results are. Only the target and what is upstream of it are considered, and a step whose code and inputs are unchanged is served from cache.
$ barca get summary pipeline.py --json[barca] 2/2 steps | done in 0.0s{"elapsed_seconds":0.08844638,"final_output":{"_barca_artifact":{"format":"json","path":".barca/artifacts/pipeline.py--summary/2d10....json","size_bytes":24}},"phases":1,"run_id":"14f3f967c1b6","status":"success","steps":[{"attempts":1,"detail":"no cached result for this code and these inputs","elapsed_seconds":0.0023,"id":"pipeline.py:raw_data","kind":"asset","reason":"not_materialized","run_hash":"a8e7...","status":"ran"},{"attempts":1,"detail":"no cached result for this code and these inputs","elapsed_seconds":0.0007,"id":"pipeline.py:summary","kind":"asset","reason":"not_materialized","run_hash":"2d10...","status":"ran"}],"steps_executed":2,"warnings":[]}The first line is progress, on stderr. The JSON is one line on stdout (hashes shortened here).
final_output is a pointer to the artifact, {"_barca_artifact": {"path", "format", "size_bytes"}}, whatever its format. Run it again and every step is "status":"cached", with
its artifact path, and "steps_executed":0. In a terminal the same run prints a summary and
the path, with a small json value (up to 10 lines) previewed under it:
$ barca get summary pipeline.pyRun 14f389a807a2 | got 'summary' in 0.050s (0 steps, 1 phase)
.barca/artifacts/pipeline.py--summary/2d10....json { "count": 3, "total": 6 }To read the result:
barca get summary pipeline.py -o value # only the value, pretty-printedbarca get summary pipeline.py -o value --json # the JSON result with the value in final_outputbarca get summary pipeline.py -o path | xargs cat # only the path, one line per artifactbarca get summary pipeline.py -o url --expires 12h # a signed HTTPS link to the result in the store-o value reads json, parquet, CSV and pickle with the reader barca.get() uses; a table
prints as a list of row objects. -o path is the default spelled out: with --json it prints
the default JSON. Up to 0.24 final_output was the value of a json result and a terminal
printed the value; -o value prints it now. -o url (experimental) prints a signed HTTPS link to
each result in a cloud store, valid for --expires (default 1h, at most 7d); with --json
each _barca_artifact pointer gains url and url_expires_at. See
Remote storage: signed URLs.
Partitioned targets. A partitioned asset returns every key, sorted: final_output is an
object keyed by partition key ("region=emea"; a compound key is "day=d1,region=emea"), each a
pointer, or a value under -o value, and -o path prints one line per key.
--partition region=emea,amer selects keys across the whole run: every partitioned node with
that dimension, upstream of the target and downstream, computes only those keys, they are
returned, and --refresh refreshes only them. A JSON object selects on several dimensions,
--partition '{"region": "emea", "day": ["d1", "d2"]}'. A selected key has the same run hash as
in a full run, so a later full run serves it from cache. A collect() fan-in over a narrowed node
(and what is below it) is not run: status skipped, reason partition_selection, one note on
stderr, and the exit code is unchanged. With no target the selection applies to every asset and
sensor; barca run and barca plan take it too. JSON gains partition_selection (the
selection, each narrowed node’s selected and skipped key counts, skipped_nodes). A
dimension no node in the cone has, a value no node with that dimension has, or a target the
selection would skip is a usage error (exit 2). Up to 0.29.2 --partition narrowed only the
target. See Parametrized Assets and Partitions. When some keys fail the run
fails (exit 1, failed_node names the key) and final_output in the result line holds the
keys that succeeded; how a partial failure is reported may still change
(barca docs partitions).
Many partition keys
Section titled “Many partition keys”A run lists at most 1000 keys (0.30.2). When a partitioned target returns more and no
--partition selected them, final_output lists the keys this run computed and counts the
ones it served from cache, so a tick that recomputed one key of 200k returns one path:
{"_barca_partitions": {"keys": 200000, "computed": 1, "cached": 199999, "truncated": false, "outputs": {"retailer=r000042": {"_barca_artifact": {"path": "...", "format": "json", "size_bytes": 49}}}, "hint": "..."}}outputs holds pointers, values under -o value or links under -o url, for at most 1000
computed keys (truncated when a cold run computed more). -o path and the table print those
paths and a note on stderr. --partition returns every key it selects, however many, and
barca sql reads every current key’s rows. barca.get() raises for such a target; pass
partition=. The same object is the outcome barca serve records for the run.
With no target (barca get pipeline.py, or barca get), barca gets every asset and sensor and
returns the last asset’s artifact. Tasks are skipped, and stderr names them and the barca run
command. A sensor nothing depends on still runs. A file with only tasks gets nothing: exit 0,
"steps": [], and a stderr note pointing at barca run.
Every get/run usage error exits 2. An unknown or ambiguous target (and run without one)
ends with Run `barca list <files>` to see available assets and tasks.; the others state
their own fix: the corrected command for the wrong order, barca run for a task given to
get, the valid names for an unknown --refresh name. A name close to one barca knows
(a target, a --refresh name, a flag or a subcommand) is preceded by Did you mean '...'?: the
best match within one edit (two for a name longer than four characters), or two that tie. The
exit code is still 2 and nothing runs.
| Flag | Meaning |
|---|---|
--refresh a,b |
Recompute the named assets (one comma-separated list) and everything downstream of them in the target’s cone. The target itself may be named. |
--no-cascade |
With --refresh: recompute only the named assets. Downstream assets stay cached and do not reflect the refresh; barca warns. |
--refresh-all |
Recompute every asset in the target’s cone. |
--dry-run |
Report each step as cached or to run, and why, without running or writing anything. |
--json, --pretty |
Force JSON or human output. |
-o path |
Print only the artifact paths, one per line (one per partition key). With --json, the default JSON. |
-o value |
Print only the value. With --json, the JSON result with the value in final_output. |
-o json|pretty |
Older spellings of --json / --pretty. |
--partition KEY=V[,V] |
Select partition keys across the cone (a JSON object for compound keys): every partitioned node with that dimension computes only them, and fan-ins over them are skipped. Works without a target. |
--agent |
Plain progress lines on stderr in place of the progress bar. |
--fields a,b |
Keep only these keys on each entry of steps: id, kind, action, status, reason, detail, changed, run_hash, artifact, warning, partitions, env. |
--env <name> |
Use a named environment’s cache and history. |
--no-cache was removed in 0.24.0; use --refresh-all.
A dry run of the same target:
$ barca get total pipeline.py --dry-runDry run: barca get total (nothing executed, nothing written)
STATUS WHY STEPwill run no cached result for this code and these inputs pipeline.py:orderswill run no cached result for this code and these inputs pipeline.py:total
2 will run, 0 cached, 0 unknownAn asset that reads a @sensor is cached against the sensor’s value: when the sensor returns a
new value, the asset and everything downstream of it re-run. --dry-run predicts from the
sensor’s last recorded value and says so in detail; before the sensor has ever run, its
consumers are unknown (reason: "sensor_output_unknown").
An asset that reads an @asset(cutoff=True) is cached against the bytes that asset wrote, not
its run hash: when it reruns and writes the same bytes, what reads it stays cached. A dry run
cannot know those bytes before it runs, so its consumers are unknown
(reason: "cutoff_pending") until it has; see
Early cutoff.
Each entry in the result’s steps array says what happened to that step. A node that declares
environment variables with env=[...] also carries env, the values the step was hashed with
(null when unset, <redacted> for names like *_TOKEN), and its --agent progress line ends
with env NAME=value .... See Decorators.
Run a task and what it depends on. The task always runs; it is never cached. The assets
upstream of it are served from cache when unchanged, as with barca get. run takes the same
flags as get. In a terminal it prints the task’s value under the summary; -o value prints
only the value.
$ barca run notify pipeline.py[barca] 1/3 steps | done in 0.0sRun 14f1e316840c | ran 'notify' in 0.074s (1 step, 1 phase)
Value:{ "sent": 80.5}barca run deploy pipeline.py --refresh fetch,transform # recompute these and everything downstream of thembarca run deploy pipeline.py --dry-run --refresh fetch # preview the cascadeTo have assets pick up outside data that changed, put a @sensor in front of them
(see Decorators).
Several targets
Section titled “Several targets”barca run and barca get take one comma-separated list of targets (no spaces):
barca run validate_registry,validate_names pipeline.py # both tasks, one runbarca get summary,orders pipeline.py --dry-run # preview the union- The union of the targets’ cones is planned once, so a shared upstream step runs (or is served from cache) once.
- Every name is checked before anything runs; an unknown name, a task passed to
get, or an empty name (a,,b) is a usage error (exit 2). - Every target runs even if another fails. A failure skips only the steps that depend on it
(
"status": "skipped", reasonupstream_failed), and the exit code is 1 if any target failed. - With several targets,
final_outputis replaced bytargets, keyed by target name in the order given:
{"status": "success", "run_id": "...", "elapsed_seconds": 0.2, "steps_executed": 3, "phases": 2, "steps": [...], "targets": {"validate_registry": {"status": "success", "final_output": {"models": 2}}, "validate_names": {"status": "success", "final_output": {"lowercase": true}}}}A failed target is {"status": "failed", "failed_node": "pipeline.py:...", "error": "..."}.
--dry-run with several targets reports targets in place of target, each with a summary
counted over that target’s cone. The full shapes are in barca docs agents.
The document includes plan_version: 1, independent of the package and database versions.
Consumers of saved plans should reject unsupported versions.
Parse the source files and print the execution plan as JSON, without running anything.
The pool size matches execution: a positive BARCA_POOL_SIZE, else [execution] pool_size, else
the available cores, or the capacity when it is smaller; pool_size and pool_source say which.
Experimental: the layout follows the planner and may change between releases. Each phase’s
reason is {"type": "initial"} or {"type": "fan_in", "node_id": "..."}. Planning reads no
state, so plan takes no --env and does not say what is cached; use --dry-run or
barca status for that. Its only option is --help.
$ barca plan pipeline.py{ "plan_version": 1, "total_steps": 3, "phases": [ { "reason": { "type": "initial" }, "streams": [ { "stream_id": "p0-w0", "steps": [ "pipeline.py:orders", "pipeline.py:total", "pipeline.py:notify" ] } ] } ], "warnings": []}Run timing
Section titled “Run timing”Run JSON includes steps[].elapsed_seconds and attempts for executed steps, including
failures; cache hits and dry runs omit them. Elapsed time sums active worker time across
attempts, including import/input loading/call/serialization and excluding retry backoff.
After a worker disconnect, the coordinator’s observed in-flight time is used. For
partitioned steps it sums executed keys, while attempts is the maximum for one key.
Under --agent (and in every run barca serve starts) a worker prints
[barca] importing <module> before loading a module; otherwise it prints nothing. Imports longer
than one second add import_seconds, included in elapsed time and summed across
attempts/keys. Reused modules add no import time. Agent completion and permanent failure
lines append attempts=N for retries. history --json sums recorded materialization
attempts (including existing upload-failure retry counts); stats retains each
materialization’s attempts and final-attempt duration. Timeout errors name the step id.
history
Section titled “history”After verifying that the process or container has stopped, explicitly reconcile an uncertain
row with barca history --reconcile RUN_ID --json. This records only that row as
interrupted and prints it in the normal history envelope (runs, total: 1,
truncated: false); --fields and human output also work. It refuses a proven live owner,
a terminal row, an unknown id, and combining --reconcile with --limit or --all.
It preserves recorded step counts, successful materializations and artifacts, and leaves
unknown end time and duration as null. It does not stop a process. Ordinary history reads
still keep an uncertain owner running; reconciliation is your assertion that it stopped.
The change is local to the selected environment’s metadata database.
Show recent runs from .barca/metadata.db: run id, command, target, status, step counts and
timing. TARGET is the run’s job by function name, all for a run without targets, cut at 24
characters with ….
The human table defaults to the 10 most recent; JSON returns every run unless --limit is explicit.
$ barca historyRUN_ID CMD TARGET STATUS STEPS CACHED TIME STARTED------------------------------------------------------------------------------------------------------14f1e316840c run notify success 1 2 0.1s 2026-10-21 02:39:25 UTC14f01f296a75 get country_report,weekly_s… success 0 2 0.0s 2026-10-21 02:39:25 UTC14f3870e173f get all success 2 0 0.1s 2026-10-21 02:39:25 UTCTimes are UTC. --fields takes run_id, command, files, target, job, status,
steps_total, steps_executed, steps_cached, attempts, started_at, finished_at,
elapsed_seconds, triggered_by, scheduled_at, schedule_id, client_run_id, failure. triggered_by says what
started the run: manual (a command typed on a machine), api (an HTTP request to
barca serve), schedule (a cron tick, with the tick in scheduled_at, RFC 3339 in UTC, and
the scheduled node in schedule_id) or client; it is null for runs recorded before barca
recorded it. job is the run’s job in full: all, or its resolved node ids sorted and
comma-separated (null for runs recorded before 0.29.0); target is the target as typed.
barca history --fields run_id,triggered_by,scheduled_at # what started each runbarca history -l 25 # last 25; human default 10, JSON default every runbarca history --json # {"runs": [...], "total": N, "truncated": bool, "hint"?: "..."}barca history --fields run_id,status # JSON with only these keys per runHistory JSON returns every run unless --limit N is explicit; the human table defaults to 10.
When more runs exist than are shown, the JSON has "truncated": true, the total, and a hint;
the table prints the same hint as one line on stderr. See Bounded output. Each
run’s files is an array of the .py files it was given.
Terminal status and successful/failed step rows commit together. Required database-write failures report an infrastructure error and preserve previous progress/history and run state, which normally remains unfinished. Captured output is written separately after that transaction.
Mid-run database writes commit completed-step rows and their progress count together. Failed batches are retained and retried without waiting for another step; replay does not count a completed step twice. Progress can be absent while the database cannot commit. The final write still records completed results not saved during execution.
Finding and reading a failed run
Section titled “Finding and reading a failed run”Filters combine with AND, and --limit, total and truncated count the runs they keep:
--status (comma-separated: success, failed, cancelled, error, running,
interrupted), --target NAME (the run’s target or job is that node; runs of every node are
left out), --triggered-by (comma-separated: manual, api, schedule, client) and
--since (30m, 24h, 7d, 2w, an RFC 3339 time or a UTC date). A failed run’s row ends
with its first failed step and the first line of its error; JSON runs carry it as
failure: {node, error} and as the flat failed_node and error (null otherwise), so
--fields run_id,failed_node,error needs no nesting.
barca history --status failed --since 24hbarca history --target clean --triggered-by schedule --jsonbarca history 836fdca186c8 # one run in detailbarca history 836fdca186c8 --json # {run, steps: [...], hint?}barca history RUN_ID prints the run’s fields, the steps it recorded (status, attempts, time,
peak memory, run hash, artifact path, format and size), and for each failed step its error,
traceback, when the worker died how it ended (worker exit: SIGKILL (possibly the OOM killer)), and the last 20 lines it printed (output_tail in JSON, without the traceback).
Each step’s JSON has max_rss_bytes, the worker process’s peak resident memory while the step
ran (on Linux the step’s own; elsewhere only when the step raised the worker’s peak), and
cpu_seconds. Bytes DuckDB spilled to disk are not recorded: DuckDB keeps no total of them. A hint on
stderr names barca logs RUN_ID --step STEP --stream stderr. Cached steps are counted in
steps_cached, not listed: a run records no row for them. With a server configured, both
forms answer from the server; a run only this machine has is an error whose remediation is
barca history RUN_ID --local.
$ barca history 836fdca186c8run 836fdca186c8 get clean failed 0.1s 1 attemptstarted 2026-10-22 22:57:27.000 UTC finished 2026-10-22 22:57:27.000 UTCtriggered by apifiles pipeline.pysteps: 1 executed (1 failed), 1 cached (cached steps are not listed)
STEP STATUS ATTEMPTS TIME RUN_HASH ARTIFACTpipeline.py:clean failed 1 - d64e1b00647e -
pipeline.py:clean failed: ZeroDivisionError: division by zero Traceback (most recent call last): File "/srv/pipelines/pipeline.py", line 11, in clean return [{"id": o["id"], "per_unit": 10 / o["qty"]} for o in orders] ~~~^~~~~~~~~~ ZeroDivisionError: division by zerohint: barca logs 836fdca186c8 --step clean --stream stderrA run’s status is running, success, failed, cancelled or interrupted. A run records
each step as it finishes, so a running run already counts them in steps_executed, and
barca status from another terminal shows them as cached. A run whose process was killed is
interrupted (no finished_at); the next barca get reuses the steps it had recorded. A run
killed in a container is interrupted too, once a container starts again on the same .barca
volume (from 0.20.0; earlier versions left it running). For runs started with 0.20.0 or later, a run stays running
whenever barca cannot establish that its process is gone. Earlier runs retain their legacy
process-id and host-name checks. See
barca docs cache, “While a run is going, and after one is killed”, and the shared-state
limitations.
Evict cached results so .barca/ stays under its size limit ([cache].max_size or
BARCA_CACHE_MAX_SIZE, default 10% of the filesystem; see
Cache size). barca already does this after every get and
run once the limit is passed; barca gc does it now. Experimental.
barca gc --dry-run # what would be evicted, per tier, and what it leavesbarca gc # evict now, down to 80% of the limitbarca gc --max-size 5GB # a one-off tighter budgetbarca gc --max-size off # only leftovers and superseded results, all of thembarca gc --json # {size_bytes, limit_bytes, target_bytes, after_bytes, tiers, evicted, failed, restricted, dry_run}$ barca gc --dry-run --max-size 400KBwould evict 4 files (1.4 MB), leaving 342 KB of 400 KB: superseded 3 files 807 KB least recently read 1 file 549 KBEach tier that has something to evict gets a line; leftovers and held by the store appear
the same way. Under the limit it prints one line, such as nothing to evict: 284 KB of 3.4 GB.
The order is leftovers, superseded results, local copies the artifact store holds, then the
least recently read current results. --dry-run lists exactly what barca gc then deletes.
While another run is in progress on this .barca/, only leftovers and superseded results are
considered (restricted says why). --env NAME evicts only that environment’s results. A file
that cannot be deleted is skipped with a warning; exit 3 only when a metadata database cannot be
read. The remote store is never touched.
Show statistics for one node: how many times it executed, timings (average, median, p95, max), how many executions repeated unchanged inputs, and recent runs. The target is required. Times are UTC.
$ barca stats total pipeline.pyAsset: pipeline.py:totalTotal materializations: 1Timing: avg 0.001s median 0.001s p95 0.001s max 0.001sRe-executed with unchanged inputs: 0.0%
Recent runs: ELAPSED STATUS ATTEMPTS CREATED 0.001s success 1 2026-10-21 02:39:25 UTCA cache hit executes nothing, so it is not counted here; barca history counts cache hits per
run. In the JSON the re-execution share is cache_hit_rate (the key keeps its name).
The JSON has id, total_runs, cache_hit_rate, avg_elapsed_seconds,
median_elapsed_seconds, p95_elapsed_seconds, max_elapsed_seconds, max_rss_bytes and
median_max_rss_bytes (peak memory over the recorded executions) and recent_runs (each
elapsed_seconds, status, created_at, error_message, attempts, run_id, cpu_seconds,
max_rss_bytes). run_id names the run, whose every step barca history RUN_ID lists.
barca stats summary pipeline.py --json # one JSON object; the node id is `id`barca stats summary pipeline.py --fields status,error_message # JSON; trims recent_runs entriesbarca stats summary --fields run_id,elapsed_seconds,max_rss_bytes # each recent run's id, time and peak memoryStart an HTTP server with a JSON API, the cron scheduler and the web UI at /ui/. It binds to
127.0.0.1 by default; --host 0.0.0.0 listens on every interface. It has no authentication. See Server API for the
endpoints and Deploying for running it behind nginx.
barca serve pipeline.py # default port 8274barca serve pipeline.py --host 0.0.0.0 # every interface (containers, VMs); no authbarca serve --timezone utc # every file in the project; cron evaluated in UTCbarca serve pipeline.py --drain-timeout 120 # on SIGTERM, runs in flight get 2 minutesbarca serve pipeline.py --max-runs 2 # at most 2 runs at once; the others wait in the queuebarca serve pipeline.py --capacity 4 # the weight= of every run's running steps adds up to at most 4barca serve pipeline.py --run-timeout 3600 # stop a run that takes longer than an hour| Flag | Default | Meaning |
|---|---|---|
-p, --port <PORT> |
8274 |
Port to listen on. |
--host <IP> |
127.0.0.1 |
IP address to bind on; 0.0.0.0 (or ::) listens on every interface. |
--watch |
off | Re-parse the DAG when a source file changes. Files added later still need a restart. |
--no-schedule |
off | Do not fire Schedule(...) nodes. |
--timezone <TZ> |
local |
Timezone for cron: local, utc or an IANA name such as America/New_York. Any other value is a usage error (exit 2). |
--read-only |
off | Refuse runs, never schedule, read the metadata DB from copies. |
--drain-timeout <SECONDS> |
[server] drain_timeout, else 30 |
On SIGTERM or SIGINT, how long runs in flight get to finish before they are put back in the run queue for the next start. 0 requeues them at once. |
--max-runs <N> |
[server] max_runs, else one per core |
How many runs execute at once; the others wait in the run queue, in order (experimental). |
--capacity <N> |
[server] capacity, else none |
The most the weight= of the running steps of every run may add up to, admitted first come, first served across runs; GET /queue shows it (experimental). |
--run-timeout <SECONDS> |
[server] run_timeout_seconds, else none |
Stop a run that takes longer, recorded as an error naming the setting. 0 is no limit; Schedule(..., timeout_seconds=N) wins for its ticks. |
--env <name> |
default |
Use a named environment’s cache and history. |
Every accepted run is recorded in the run queue before the server answers, and a restart resumes
the queued runs in order under their run ids. SIGTERM or SIGINT drains the server: no queued run
starts (new ones are still accepted and queued), the scheduler stops, /health reports
draining, and runs in flight finish, uploads included. The exit code is 0 when they all
finished, and 0 when --drain-timeout passed and the runs left were put back at the head of
the queue (they start again from the beginning after the restart). A second signal cancels them
instead (recorded as cancelled, reason server shutdown): exit code 1. See
Server API and
Restarting without losing work.
A barca server’s run queue: the runs executing and the runs waiting to start, in order. It needs
a server (--server URL, BARCA_SERVER or [server] url); without one it exits 2. Experimental.
barca queue --server # running and queued runs (a table in a terminal)barca queue --server --json # {paused, paused_at, draining, max_runs, running, queued}barca queue pause --server # queued runs wait; runs in flight go on; kept across restartsbarca queue resume --server # queued runs start again, in orderpause and resume answer with the queue after the change, and exit 2 on a --read-only
server. See barca docs scheduling, “The run queue”.
With a remote store, barca serve backs its metadata DB up every minute it changed (not with
--read-only), and barca state restore refuses to run while it is up. See
Remote storage.
List every asset, task and sensor barca finds, with its kind, freshness and inputs. A scheduled
node also shows its cron and its next fire time, to the second. The time is the next match of
the cron expression in the local time of the machine list runs on (the table column is
NEXT FIRE (LOCAL TIME)). list talks to no server, so it does not know a server’s
--timezone: a server started with one fires at the same wall-clock time in that zone, and its
GET /schedule reports the times it will fire at.
$ barca list pipeline.pyNAME KIND FRESHNESS DEPS------------------------------------------pipeline.py:orders asset always -pipeline.py:total asset always pipeline.py:orderspipeline.py:notify task always pipeline.py:totalThe FRESHNESS column shows what the decorator declares. Only schedule has an effect, and only
under barca serve.
barca list # every node in the projectbarca list pipelines/ # only files under pipelines/barca list pipeline.py --json # {"nodes": [{id, kind, freshness, schedule?, inputs, env, next_fire?}], "total", "truncated", "root"}barca list pipeline.py --fields id,inputs # JSON with only these keys per nodeIn JSON, freshness is always, manual or schedule (lowercase, like kind); a scheduled node
also has schedule, its cron expression. list reads no state, so it takes no --env.
list prints every node in JSON by default, in topological order; human tables show at most 100; see
Bounded output.
When any node declares environment variables (@asset(env=["SOURCE_CSV"])), the table gains an
ENV column listing them; --json always includes env (an empty list when none are declared).
Bounded output
Section titled “Bounded output”List-shaped commands (list, status, history) return every item in JSON unless --limit N
is explicit, including piped output and --fields. Human tables show at most 100 nodes or
10 history runs by default; --all removes the bound. Their JSON keeps its envelope:
{"nodes": [...], "total": 312, "truncated": true, "hint": "pass --limit N for more, or --all for all 312 nodes"}truncated and total are always present, hint only when truncated. --limit N and --all
choose how many items to print.
--fields a,b keeps only those keys on each item of any JSON output: nodes (list, status), runs
(history), recent_runs (stats), steps (get/run, including --dry-run) and topics
(docs). It implies JSON everywhere; combining it with --pretty, -o pretty or run -o value is
a usage error (exit 2). An unknown key is a usage error (exit 2) that lists the valid ones. Limits bound
how many items are printed, never their content: error messages and tracebacks are always
complete. plan has no per-item objects (its steps are id strings), so it takes no --fields.
status
Section titled “status”Show, for every node in scope, its kind, inputs, whether it is partitioned, its cache state with the reason, its last run, and the shape of its result. It runs nothing.
barca status total pipeline.py # only `total` and its upstream conebarca status total,notify pipeline.py # several targets: the union of their conesbarca status pipeline.py --json # {target, targets, nodes, summary, total, truncated, root}barca status pipeline.py --json --sample 5 # plus up to 5 sample rows per json/parquet/CSV artifactbarca status --problems # only nodes that need attentionbarca status --state stale,never_run # only these cache states--problems keeps every node that is not fine: every state but cached and always_runs, a
task or sensor whose last run failed, and a schedule that is paused or failing (max_failures
reached). --state keeps the named states. The summary still counts every node, and the JSON
names the filter in filter. A node whose last run failed shows the first line of its error in
WHY (failed: ZeroDivisionError: division by zero); the whole message is
last_materialization.error. A scheduled node has schedule (cron, next_fire,
failures, max_failures, failing, paused, paused_until, last_status, live) and the
table a SCHEDULE column: from a server it is the server’s scheduler’s view, the numbers of
GET /schedule and the web UI; on this machine it is what the metadata DB holds, with
live: false. See Agent Skill and barca docs agents,
“Debugging pipelines”.
NAME KIND STATE WHY LAST RUN SHAPE DEPSorders asset cached materialized success 2026-10-21 02:39:25 UTC 0.00s 3 rows x 2 cols -total asset cached materialized success 2026-10-21 02:39:25 UTC 0.00s dict (1 key) ordersnotify task always-runs task success 2026-10-21 02:39:25 UTC 0.00s dict (1 key) total
2 cached, 0 stale, 0 never run, 0 partial, 0 unknown, 1 always runCache state (cache.state) is the decision --dry-run makes, from the same code path. JSON
spells it in snake_case, exactly like the summary keys; the table prints never-run and
always-runs:
| state | meaning | reasons |
|---|---|---|
cached |
a successful result matches this code and these inputs | materialized |
unsynced |
cached and computed here, but its upload to the artifact store failed after the step succeeded; the next get or run uploads it (remote storage) |
local_only (table: local only) |
stale |
ran before, but would run again | changed, upstream_stale, failed, artifact_missing, evicted |
never_run |
no successful materialization recorded | no_record, failed |
partial |
partitioned, some keys cached | partitions_missing |
unknown |
dynamic partitions whose source has not run | partitions_unknown |
always_runs |
tasks and sensors | task, sensor |
changed means the run hash differs from the last successful materialization, and
cache.changed says what differs: code, decorator argument NAME, decorator NAME,
helper FILE:NAME (a function or constant the code uses, in its own file or a project module),
input ID, sensor ID, env NAME or kind. detail and the table’s WHY column name the first
and count the rest (changed: helper pipelines/common.py:bucket_ltv (+1 more)), and a dry run’s
step carries the same changed list. A result recorded before 0.29.0 has no record of the parts,
and its node stays a plain changed.
artifact_missing means the result is recorded but its artifact file is gone and a run would
have to read it, so it would be computed again; a missing artifact that nothing reads leaves the
node cached (barca docs cache, “A cached result whose artifact is missing”).
Last materialization is the most recent execution recorded in the metadata DB, success or failure. Cache hits do not change it. For a partitioned node it is the latest run’s first key in sort order, so identical runs report the same key.
Shape is read from the artifact file only, by a small Python helper; your code is never
imported. Parquet gives rows and columns from the file footer (needs pyarrow), json gives
type and, for a list or object, rows, columns or keys, and pickle gives type only.
Remote artifacts are read from the store; a failure is reported in shape.note and the
command still exits 0. Details: barca docs status.
Partitioned assets appear as one node with partitions: {total, cached, missing, missing_keys} (up to 20 keys). last_materialization is the most recently run key (named in its
partition field), and shape describes that key’s artifact.
Each node also lists env, the environment variables it declares with env=[...]. Like list,
status returns every node in JSON unless --limit N is explicit. Its human table shows at most
100 nodes unless you pass --limit N or --all; the summary still counts
every node, and the JSON reports total and truncated (see Bounded output).
With an artifact store, a cached unpartitioned node is cached
only when this machine’s copy, the store’s object and the recorded digest agree; otherwise it is
unsynced with cache.reason local_only, store_only or mismatch, and barca get repairs
it without running a step.
Status reads the metadata DB as it is at that moment: while a barca get is running, the
steps it has finished already show as cached (a partitioned asset as partial).
Status executes no pipeline step and records no new run; partitions(<expression>) may evaluate Python while
resolving keys. An unknown target
is a usage error (exit 2). See barca docs status.
Query cached results with DuckDB. Every asset, sensor and task with a result on disk is a view
named after its declared name=, or its function when unset; a partitioned asset is one
view with a partition column. No step
runs, your code is not imported, and no run is recorded. Only parquet, CSV and json results are
views; a pickled result cannot be queried. Install SQL support with uv add 'barca[sql]'. With a remote store the
artifacts of the views a query names are downloaded into .barca/sql-cache/ and reused.
barca’s own records are tables in the barca schema: barca.runs, barca.materializations and
barca.nodes. With a server configured, the views and those tables come from the server’s
ledger and the query runs on your machine; the server never runs SQL. Experimental.
$ barca sql "select region, sum(amount) as amount from orders group by 1 order by 1"region amountamer 70.5emea 10.0| Flag | Default | Meaning |
|---|---|---|
-l, --limit <N> |
100 |
Maximum number of rows returned. |
--all |
off | Return every row. Cannot be combined with --limit. |
--json, --pretty |
by terminal | Force JSON or the table. |
--env <name> |
default |
Query a named environment’s results. |
--server [<url>] |
the configured server | Resolve the views from that server’s ledger; the query runs here. |
--local |
off | Query this machine’s own results and records, ignoring a configured server. |
See barca sql for views, errors and limits.
With a remote store ([remote] uri or BARCA_REMOTE_URI), get and run back the metadata
DB up on a one-minute checkpoint and when they end, and serve every minute it changed: to
.barca/backups/<name>.db and <uri>/<env>/state/backups/<name>.db, five copies kept in each,
each with a <name>.json sidecar (run_id, rows, digest, barca_version, db_format,
and from 0.29 host and project: the machine and project directory that took it). Nothing is
downloaded before a run. A store is one machine’s backup: when another machine’s backup is
newer than this one’s own, a backup or barca state backups warns once, pruning keeps each
machine’s newest backup, and restore without --from picks this machine’s.
barca state backups # newest first: # NAME WHERE SIZE ROWS RUN HOSTbarca state backups --json # {backups: [{index, name, where, size_bytes, run_id, rows, digest, agree, barca_version, db_format, host, project, mine}], local_dir, store}barca state restore # put this machine's newest backup back (the newest, if it has none)barca state restore --from 1 # the newest, whichever machine took itbarca state restore --from 2 # by index; or --from 2026-10-14T18-02-11.482Z-3f9a1c2ebarca state reset --yes # move .barca/metadata.db aside to metadata.db.reset-<UTC>, start emptybarca state restore --from .barca/metadata.db.reset-2026-10-20T18-02-11.482Z # undo a resetrestore checks the copy first (a whole SQLite file, integrity, barca’s tables, a usable
format), the local copy before the store’s, then moves the current database to
.barca/metadata.db.pre-restore and puts the copy in place; one line on stderr says what it
restored and from where, and --json adds {restored, from, source, pre_restore, rows, run_id, host, project}
on stdout. It never runs by itself. Exit 2: no backup, an unknown --from, or a copy that fails
the check (nothing is changed). Exit 3: barca serve is running on the environment. See
Remote storage.
reset (experimental) needs no store: it moves the database and its -wal and -shm aside to
.barca/metadata.db.reset-<UTC> in the same directory and creates an empty one, for a database
that cannot be opened or migrated or a history you want to start over. Nothing is deleted and
artifacts are left alone. It asks first in a terminal; --yes skips the question and is required
when stdin is not a terminal (exit 2 otherwise). --json prints {database, moved_to, moved, undo}. Exit 3: a run is in progress on this machine, or barca serve is running on the
environment (use its UI’s Reset database button or POST /state/reset). Undo with
barca state restore --from <the moved file>, which also takes any database file’s path.
The manual, compiled into the binary, so it works offline and describes the installed version.
Every command’s --help ends with examples.
barca docs # topic index with one-line summariesbarca docs types # one topic as markdown (output formats, annotations, duckdb)barca docs examples/duckdb # a runnable example pipelinebarca docs skill # the agent skill (SKILL.md), with its frontmatterbarca docs --all # every topic in one streambarca docs --json # topic index as JSON; add a topic for its full textbarca docs --fields name # JSON index with only topic names (name, summary, content)barca docs cache --pretty # markdown, the default even when pipedbarca docs skill is the short agent skill an AI agent loads once.
barca docs contract is the CLI contract. barca docs agents
describes the output contract for scripts and AI agents.
Errors and exit codes
Section titled “Errors and exit codes”| Code | kind |
Meaning |
|---|---|---|
| 0 | success | |
| 1 | step_failed |
a step of yours raised; the traceback is included and the run is recorded as failed |
| 2 | usage |
bad flags or arguments, unknown target, get on a task or run on an asset, unreadable or invalid .py file, invalid --env or barca.toml |
| 3 | infra |
barca or its environment failed: metadata DB, worker pool, remote state, I/O |
| 130 | cancelled |
stopped by Ctrl-C (SIGINT) or SIGTERM |
A closed stdout or stderr never makes barca panic, never stops a run and is not an error:
output for the closed stream is dropped, a get or run finishes and is recorded, and the
exit code is the one the command would have had with a reader (barca list | head -1 exits 0).
See the CLI contract.
An error in a pipeline file fails every command that reads the file with exit 2, whatever the
target: a syntax error, or a decorator called with an argument it does not define
(@asset(after=other), input= for inputs=). See
Accepted arguments. A syntax error in a helper
module a pipeline file imports fails, with exit 2 before anything runs, every command that plans
a step of that file (plan, status, get, run, --dry-run), naming the module, line,
column and source line; list and steps of other files are not affected.
In JSON output mode (whenever results are JSON: piped or captured stdout, --json, -o json or
BARCA_OUTPUT=json; plan always; docs with --json), an error is a single JSON line, the
last JSON error object on stderr (a failed run adds its summary afterwards):
{"code":2,"error":"Asset 'nope' not found. Available: pipeline.py:orders, pipeline.py:total, pipeline.py:notify","kind":"usage","remediation":"Run `barca list pipeline.py` to see available assets and tasks."}error, code, kind and remediation are always present. When a step fails (kind: "step_failed") the envelope also has node (the failing step’s id), traceback (the Python
traceback of your code, with barca’s own frames removed), artifact_dir (where that step’s
artifacts are stored; a local path or remote URI, which may not exist if the step never
succeeded) and phase (where the step failed: user_code for your code; load_inputs,
split_output, write_output or sink for barca’s own work around it, whose traceback keeps
barca’s frames and whose prose says failed in barca; null when unknown, such as a worker
that died):
{"artifact_dir":".barca/artifacts/pipeline.py--clean","code":1,"error":"step 'pipeline.py:clean' failed: ZeroDivisionError: division by zero","kind":"step_failed","node":"pipeline.py:clean","phase":"user_code","remediation":"Fix the error in 'pipeline.py:clean' (see the traceback) and re-run the same command. Steps that succeeded are cached and will not re-run.","traceback":" File \"/home/you/project/pipeline.py\", line 11, in clean\n return x / 0\n ~~^~~"}In human mode (a terminal, --pretty, -o pretty, -o path or -o value) the error is prose, with the remediation
on the last lines. Errors never go to stdout. The Python API raises barca.BarcaError with the
same fields as attributes (kind, code, remediation, node, traceback, artifact_dir,
phase).
When a step fails in JSON mode, get/run still print one result line on stdout, so you can
read the outcome without parsing stderr: {"status": "failed", "failed_node": ..., "error": ..., "run_id", "steps", ...}, where the failed step’s status is failed. A successful result has
"status": "success". After the error, stderr ends with one greppable line:
[barca] run failed: step 'pipeline.py:clean' failed: ZeroDivisionError: division by zero; 0 downstream steps skipped (exit 1).
A worker that dies
Section titled “A worker that dies”A worker process that dies while it runs a step (the kernel’s out-of-memory killer, a segfault
in a C extension, os._exit() in your code) fails the step with exit 1, and the error names
how the process ended, in the result JSON, the envelope and the run failed: line:
worker for pipeline.py:train exited with SIGKILL (possibly the OOM killer)worker for pipeline.py:train exited with SIGSEGVworker for pipeline.py:train exited with code 3SIGKILL is what the out-of-memory killer sends: check the container’s or host’s memory limit.
Several heavy steps running at once in separate workers add up; give them a weight= so fewer
run at once (see Decorators). A worker frozen waiting for
its parallel() branches adds while it waited for its parallel() branches. When the exit
status is not available within a few hundred milliseconds, the error is the plain
worker disconnected.
version
Section titled “version”$ barca versionbarca 0.27.0barca --version prints the same. Neither takes --json.
get, run, status, sql, history, stats, serve, state backups, state restore and state reset accept --env <name> (default:
BARCA_ENV, then default_env in barca.toml, then default). plan and list read no
state and reject it. Each environment has its own cache, artifacts, history and remote
location. get, run and serve print the environment and artifact store once at startup
on stderr when default_env or an artifact store is in effect. See
Configuration.
Behavior changes from earlier versions
Section titled “Behavior changes from earlier versions”getandrunprint a human summary in a terminal; they used to print JSON there. Piped or captured stdout is still JSON.list --jsonandhistory --jsonprint an envelope (.nodes,.runs); up to 0.9.0 they printed a bare array.barca runserves upstream assets from cache; it used to recompute all of them. Pass--refresh-allfor the old behavior.--burst a,bis now--refresh a,b.--refreshcascades downstream; it used to recompute only the named assets. Pass--no-cascadefor the old behavior. A step re-run by the cascade reportsreason: "refresh_cascade".- A sensor’s value is part of its consumers’ run hashes; it used not to be. Assets that read a sensor re-ran once after that upgrade.
- From 0.20.0: SIGTERM stops
get,runandservethe way Ctrl-C does.getandrunexit 130 (cancelled) and record the run ascancelled; before, SIGTERM killed barca at once (a shell reported 143) and the run was later reported asinterrupted. As process 1 of a container barca used to ignore SIGTERM. - From 0.20.0:
barca serve --timezonewith a value barca does not know exits 2; it used to print a warning and use local time. - From 0.20.0:
POST /run/{target}andPOST /get/{target}answer 404, 409 or 400 for a target that cannot run; they used to answer 200 with arun_idof a run that then failed. - From 0.20.0: the
barca listtable columnNEXT FIREisNEXT FIRE (LOCAL TIME). - From 0.25.0:
getprints artifact paths by default.final_outputin the JSON is always a pointer, and a terminal shows the path with a small json value previewed under it;-o valueprints the value. A partitioned target returns every key, and--partitionselects keys. (From 0.30.2, past 1000 keys only the keys a run computed are listed.).barca/has a size limit ([cache] max_size, default 10% of the filesystem) and is evicted past it. - From 0.26.0: a remote store fails soft once the steps are done: an upload that fails after
every step succeeded exits 0 with a warning (it was 3), and the result is
unsynceduntil the next command uploads it. - From 0.26.0: the shared history (
optimisticmode) is gone.[remote] state,state_uri,push_retries,BARCA_STATE,BARCA_STATE_URIandBARCA_PUSH_RETRIESare usage errors; nothing is pulled before a run, and the store holds backups instead (Remote storage).barca serveworks with a store. - From 0.27.0:
barca servedrains on SIGTERM or SIGINT: runs in flight finish, for up to--drain-timeoutseconds, before it exits; it used to cancel them at once (serve). - From 0.29.4: a draining
barca serveaccepts new runs into its run queue (202,pending) instead of refusing them with503, and at--drain-timeoutit puts the runs left back in the queue (exit 0) instead of cancelling them (exit 1); only a second signal cancels them.--serverclients andlogs -f --serverwait up to 60 seconds (BARCA_SERVER_RECONNECT) for a restarting server instead of exiting 3 at once (serve).
Recorded output
Section titled “Recorded output”barca logs RUN_ID --pretty prints what a run’s steps wrote, without progress or prefixes:
their stdout, and their Python stderr (logging, warnings, sys.stderr) with the traceback
of each failed attempt. Writes straight to file descriptors 1 and 2 (native code, subprocesses)
are not recorded.
Without a run id, barca logs reads the most recent run (running or finished) and names it in a
line on stderr: barca: logs of the most recent run: 07c4a64d0540 (run notify, success, started 2026-10-21 06:40:36 UTC).
--json returns [{node_id, stream, ts, line}] in recorded order: stream is stdout or
stderr, ts when the line was written (RFC 3339 UTC, milliseconds). --stream stderr keeps
one stream, --timestamps puts the time before each text line. --step report selects a
function name; use an exact node id if names collide, or a partitioned id to select one key.
--env dev reads that environment’s history. It reads no pipeline source and contacts no
store. Unknown runs/steps and ambiguous names exit 2; silent executed steps return no lines.
Lines written before a failed step are retained. Recording stops after the first complete user line that reaches or exceeds 16 MiB of
cumulative text; the crossing line and a marker are retained, and framework notes may follow.
This is a soft threshold, not a hard byte or memory bound.
barca logs -f follows a run: it prints lines as they arrive (one JSON object per line with
--json) and exits when the run ends, 0 when it succeeded, 130 when it was cancelled, 1
otherwise. Without a run id it follows the newest running run.
barca run report pipeline.py # in one terminalbarca logs -f --step report # in anotherbarca logs --step report --stream stderr --timestamps # after a failure: its logging and tracebackWith a server configured (or --server), barca logs reads the server’s runs, scheduled ones
included; --local reads this machine’s (barca docs logs, “Debugging a live pipeline”).
Task return values remain usable through barca run --json, -o value, and barca.run(...).
During get/run, non-JSON modes print stdout lines to stderr with [node_id] prefixes;
--agent framework lines keep [barca] prefixes. JSON output suppresses captured prints;
retrieve them with barca logs using the result’s run id.
See Doctor and local debugging for prerequisite checks and a local-only debugging profile.