> ## Documentation Index
> Fetch the complete documentation index at: https://docs.transluce.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching and reruns

> When a step reuses an earlier result, when it runs again, and how to control it.

Model calls are slow and expensive, so fxtr caches every step's result and reuses it whenever it
safely can. This page explains the rules, which decide what a rerun computes.

## Pathnames and cache addresses

Every operation in a job has a **pathname**: a readable address built from the names you give
things. The root workflow's pathname is the `root` you pass when launching (`"root"` by default).
Below it, each step, child workflow, and source array adds its local name, and a mapped or
replicated call adds its keys:

| Pathname | Refers to |
| - | - |
| `word-count-v1` | The root workflow |
| `word-count-v1/count_words[prompt:"greeting"]` | One call of a mapped step |
| `word-count-v1/evaluate/sample[task:"a",sample:0]` | A step inside a child workflow, with a replica |

A step's pathname is also its **cache address**, where its result is stored. Addresses are
shared by every job in the project's database schema, which is what lets a later job reuse an
earlier job's results.

Because names form addresses, choose specific local names (`comparison_judge_config` rather than
`config`) and never put `/`, `[`, or `]` in one.

## When a step reuses a result

When a job reaches a step, it makes a **request** at the step's address. The request's
**fingerprint** is the step's registered name plus the exact input data the call receives.

| The address holds | What happens |
| - | - |
| Nothing | The step runs. |
| A completed or running request with the same fingerprint | The job reuses the result, waiting for it if it's still running. |
| A failed or abandoned request | The step runs again. |
| A completed or running request from another job with a different fingerprint | The step is **held** and doesn't run. The job stops once nothing else can proceed. |

A held step means the address already holds a result computed from different inputs. fxtr won't
silently reuse a result that doesn't match, and won't overwrite one that another job depends on,
so it stops and tells you:

```text theme={null}
reason: cache key 'word-count-v1/count_words[prompt:"question"]' is held by a different request; clear it to run this request there
```

## What this means in practice

<AccordionGroup>
  <Accordion title="Changing a step's code does not invalidate its cache" icon="code">
    The fingerprint includes the step's name and inputs, not its implementation. After changing
    what a step computes, launch under a new `root`, or clear the step's entries. fxtr does
    record the commit each job ran at, so you can always see which code produced a result.
  </Accordion>

  <Accordion title="Changed inputs are held, not rerun" icon="hand">
    Rerunning under the same `root` with edited data reuses the unchanged slices and holds the
    changed ones, along with any step downstream of them. Use a new `root`, or clear those
    entries.
  </Accordion>

  <Accordion title="Fresh samples need a new address" icon="dice">
    A new job with the same `root` and inputs reuses earlier model samples: the job's ID isn't
    part of the address. For fresh samples, generate a new root in the launcher, such as
    `root=f"word-count-{uuid.uuid4()}"`. Never generate it inside a workflow.
  </Accordion>

  <Accordion title="Addresses aren't seeds" icon="shuffle">
    Two different addresses run separately even with identical inputs. That's how replicas get
    independent samples.
  </Accordion>

  <Accordion title="A job keeps the results it used" icon="lock">
    Clearing a cache entry affects later requests only. A finished job still has the results it
    was bound to, even if a later job computes a different result at the same address.
  </Accordion>
</AccordionGroup>

## Put everything that matters in the inputs

Everything that affects a result should reach its step as an input: data, model IDs, prompts,
and sampling settings. Then changing any of them changes the step's fingerprint, and a result
computed from the old settings is never reused for the new ones.

For the same reason, pass file contents or a typed dataset rather than a file name: a step given
only a file name has the same fingerprint however the file's contents change. Keep credentials in
the environment, never in inputs.

## Clearing entries

List entries with `fxtr cache list`, and clear an address or everything under it with
`fxtr cache clear`:

```bash theme={null}
uv run fxtr cache list
uv run fxtr cache clear word-count-v1/count_words
```

After clearing held addresses, resume the stopped job with `uv run fxtr resume JOB`. From Python,
use `await client.clear_cache_entries(prefix="word-count-v1/count_words")`, or
`keys=[...]` for exact addresses.

<Tip>
  To keep trial runs apart from real results, point the project at a scratch schema while
  experimenting. See [Before you launch](/fxtr/guides/running-jobs#before-you-launch).
</Tip>

## Cache namespaces

A job's **cache namespace**, the prefix of every address in it, is its `root`. To change the
namespace of part of a job while keeping its pathnames in the viewer unchanged, pass
`override_cache_key_prefix` to `run_step` or `run_child_workflow`:

```python theme={null}
clusters = context.run_step(
    "cluster_transcripts",
    cluster_transcripts,
    {"transcripts": transcripts},
    override_cache_key_prefix="clusters/baseline-v1",
)
```

Mapped and replica keys are appended to the prefix; an unmapped step uses it directly. A child
workflow's prefix is inherited by everything beneath it. Within one job, two different requests
at one address are an error (`StepConflictError`), which usually means two runs were given the
same prefix.
