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

# Files

> Save and view files from your experiments

Pluto allows you to save arbitrary files during your experiments. This is useful for saving model checkpoints, configuration files, output data, or any other files you want to associate with a run.

## Logging Files

Use `pluto.File()` with `pluto.log()` to save a file to your experiment:

```python theme={null}
import json
import pluto

run = pluto.init(project="my-project", name="experiment")

# Write a JSON file
results = {"accuracy": 0.95, "loss": 0.05}
with open("results.json", "w") as f:
    json.dump(results, f)

# Log it to Pluto
pluto.log({"results": pluto.File("results.json")})

run.finish()
```

## Viewing Files

Files logged during an experiment can be viewed in the web UI. Navigate to your experiment, click the `Summary` tab, and visit the `Files` section.

<img src="https://mintcdn.com/trainy/KCTEI2YX1k0aZSgl/images/Screenshot2026-01-21at2.14.08PM.png?fit=max&auto=format&n=KCTEI2YX1k0aZSgl&q=85&s=7e3252c2464f5c47a455106e8dda6935" alt="Screenshot 2026 01 21 At 2 14 08 PM" width="2928" height="1788" data-path="images/Screenshot2026-01-21at2.14.08PM.png" />

<Note>
  Plain text files will only render in dropdown if they are saved with a supported filename extension (i.e. .txt, .yaml, .py, etc.). For files that don't follow this convention, the file won't be viewable via the webUI but will still be downloadable.
</Note>

## Use Cases

Common use cases for file logging include:

* **Configuration files**: Track the exact configuration used for each run
* **Output data**: Save predictions, embeddings, or other generated data
* **Logs and reports**: Save text logs or generated reports

## Related

For logging specific media types with built-in visualization support, see:

* [Images](/pluto/visualizations/images) - Log and visualize image data
* [Audio](/pluto/visualizations/audio) - Log and play back audio files
* [Video](/pluto/visualizations/video) - Log and view video files

## Interactive viewers

Some file types render as live content rather than a download link.

| Type | Renders as | Limits |
| - | - | - |
| **Plotly figures** | An interactive chart | JSON over 8 MB shows as raw text |
| **matplotlib figures** | An interactive chart — these arrive as Plotly figures | Same 8 MB ceiling |
| **3D point clouds** | An orbit/zoom viewer | Row widths 3, 4 or 6 only; over 200,000 points are decimated |
| **HTML** | A sandboxed page, with a source toggle | External stylesheets and scripts are blocked |
| **Images, video, audio** | Inline players and viewers, with [annotations](/pluto/visualizations/images#annotations) | — |
| **Text and code** | Syntax-highlighted preview | Display truncates at 500 KB or 5,000 lines |

<Note>
  **Bokeh isn't supported.** `Object3D` renders only as a point cloud; mesh and molecule formats such as `.obj`, `.glb` and `.pdb` have no viewer.
</Note>

## The Files tab

Every run has a **Files** tab listing everything it logged, grouped by log name and step.

* **Zoom** images from 25% to 800%, with annotation layer toggles
* **Step through samples** when several files share a step, in the order they were logged
* **Download what you see** — for an annotated image this is a flattened PNG including the visible overlays and a caption strip, not the raw file
* Captions appear beneath the preview

The tree lists up to 10,000 files per run.

<Note>
  The file count in the sidebar footer includes mask files, which are hidden from the tree itself. A run with 4 images and 2 masks reads "6 files" and shows 4.
</Note>

## Upload reliability and recovery

File uploads go through a background sync process, separate from your training loop, so a slow or flaky network never blocks training. If an upload hits a transient failure — a connection reset, a server 5xx, a timeout — the sync process retries it for as long as the run is active, with exponential backoff (capped around 60 seconds) plus jitter. There's no retry-count limit: a network disturbance during startup or anywhere else can no longer cause a file to be silently dropped.

During a network blip, you may see logs like:

```
WARNING  still retrying "config/run.yaml" after 63.2s (attempt 12): ...
```

That's expected and not an error — it means the file is still queued and will keep retrying. Only a small set of failures are treated as terminal and stop retrying: a non-retryable 4xx from the server (a rejected upload), or the staged local copy being deleted from disk before it could be sent. Those are logged at ERROR with the server's reason:

```
ERROR    Permanently abandoning file "config/run.yaml" after ...
```

An abandoned file is always recorded in `failed_requests.log` in the run directory, so it's visible even if you weren't watching the logs live.

`run.finish()` waits for pending file uploads, not just metrics. If anything is still unsent when the shutdown timeout elapses, `finish()` reports it honestly at WARNING instead of claiming success, along with a recovery command:

```
WARNING  2 records/files were NOT uploaded. Data preserved in SQLite at
         <run dir>/sync.db — run `pluto sync <run dir>/sync.db` to retry.
```

**Recovery.** Unsent payloads stay in the run's local SQLite staging database until they're resolved, so nothing is lost even after the process exits:

* Run `pluto sync <path-to-sync.db>` to retry everything still queued for that run from the same machine.
* Or call [`pluto.query.upload_file`](/pluto/querying-runs#uploading-a-file-to-an-existing-run) to re-attach the file out-of-band — from any machine, without reopening the run.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.