Skip to main content
Atlantic’s Cairo 1 lane runs programs with cairo1-run (--append_return_values, PIE output). A few upstream cairo-vm limitations currently break or restrict common programs. This page describes them and the available workarounds.

Dictionaries with extra builtins or untaken dict paths

Status: Broken on Atlantic until starkware-libs/cairo-vm#2389 is merged and Atlantic deploys that cairo-vm version.

What fails

Trace generation for Cairo 1 programs that:
  1. Use a dictionary (Felt252Dict / SegmentArena) together with other builtins such as Poseidon or Bitwise (for example hades_permutation or bitwise ops), or
  2. Contain dict code on a branch that is not taken for the given inputs (SegmentArena is still an implicit of main, but no dict is allocated at runtime).
Typical errors:
Programs that only use a dict (no Poseidon/Bitwise) or only Poseidon (no dict) often succeed. The combination — or an unused dict path — is what trips the runner.

Why it happens

Atlantic’s Cairo 1 path always enables --append_return_values so return values can be written into the output segment for the PIE. After main returns, that exit wrapper:
  1. Serializes outputs with CASM rescope blocks, which drop AP-relative variables, including the live SegmentArena pointer.
  2. Tries to recover SegmentArena from a hard-coded FP offset (fp + 2 * builtins.len() + 2). That cell holds the initial arena pointer (base+3), not the pointer main returned. Dict alloc/destruct copies the 3-cell header forward, so validation against the initial pointer is wrong (often a silent no-op). Extra builtins (Poseidon, Bitwise) or gas can shift that FP cell onto a felt or a segment base, which leads to RelocatableSubUsizeNegOffset / AddressNotRelocatable.
  3. Always emits the RelocateAllDictionaries cheatcode when SegmentArena is present. The dict manager exec scope is only created when a dict is actually allocated. If dict types appear in Sierra but the taken path never allocates one, the hint crashes with VariableNotInScopeError("dict_manager_exec_scope").
Upstream PR #2389 fixes both issues: it stashes the final SegmentArena pointer in a dedicated FP-stable local, and treats relocate as a no-op when no dict was allocated.

Workaround until PR is merged

Generate the PIE locally with a fixed cairo-vm, then submit that PIE to Atlantic for proof / verification (skip Atlantic’s broken Cairo 1 trace step).
  1. Use the Herodotus fork (includes the fix): https://github.com/HerodotusDev/starkware-cairo-vm/tree/main
  2. Build and run cairo1-run against your Sierra (gas disabled is recommended for this runner):
  3. Submit pie.zip to Atlantic with pieFile and the desired result (PROOF_GENERATION, PROOF_VERIFICATION_ON_L1, or PROOF_VERIFICATION_ON_L2). See Sending Query — Input: Trace File.
Once #2389 is merged and deployed on Atlantic, submitting programFile + inputFile for these programs should work again without a local PIE step.

poseidon_hash_span / corelib gas helpers (get_builtin_costs)

Status: Does not work with cairo1-run / Atlantic Cairo 1 when programs are compiled with gas checks stripped (the usual Atlantic / cairo1-run setup). This is a long-standing upstream limitation, not specific to Atlantic.

Official documentation

Documented in the upstream cairo1-run README: Compiling without gas checks removes gas-related libfuncs that the compiler would emit, but it cannot remove gas APIs that appear in Cairo source / corelib. Calls into the gas corelib module — withdraw_gas, withdraw_gas_all, and get_builtin_costs — fail under that runner.

Important: not only Poseidon hash-span

core::poseidon::poseidon_hash_span is the best-known trigger because its implementation calls get_builtin_costs. The same failure mode applies to any use of those gas libfuncs / helpers — for example other corelib paths that read the builtin cost table or withdraw gas. Typical symptom when the cost table was never set up:
(or related operand / memory failures during the gas helper).

What to use instead

Prefer hashing via HashStateTrait / hades_permutation, or a gas-free copy of span hashing (same algorithm as corelib, without get_builtin_costs). Upstream example (also under cairo_programs/cairo-1-programs/poseidon.cairo in cairo-vm):
Do not call core::poseidon::poseidon_hash_span (or other gas-table helpers) in programs you intend to run on Atlantic’s Cairo 1 Rust VM until the runner provisions gas the way Starknet does.

Large program outputs (~3700+ felts)

Status: Atlantic rejects Cairo 1 runs whose public output is larger than about 3700 felts. This is a prover limit on the serialized output segment.

What fails

Programs whose main returns a large Array<felt252> (or otherwise writes a large output segment) fail once the output length crosses roughly that threshold — even if the rest of the run is fine. Do not return the full array as public output. Return a Poseidon hash of the array instead, and decommit the preimage on-chain (or elsewhere) only if consumers need the concrete values. Avoid:
Prefer:
The public output is then a single felt (plus array length encoding), which stays well under the limit. Verifiers that need the original array can check poseidon_hash_span(preimage) == output when the preimage is supplied on-chain or off-chain.