code-runner
Guides

Multi-file input

Submit multiple files, subdirectories, and binary data with a single job.

POST /v1/execute accepts one or more input files in files[]. Every file is materialized under the sandbox workspace (/workspace) before the entrypoint runs. Files support text and binary content and subdirectory paths, all inline in the request body — no external fetch, no bucket, nothing to provision.

The FileInput shape

{
  "name": "data/input.csv",   // relative path under /workspace; "/" makes subdirs
  "content": "a,b\n1,2\n",     // interpreted per `encoding`
  "encoding": "utf8"           // "utf8" (default) | "base64"; optional
}
FieldRequiredNotes
nameyesRelative path written into /workspace. May contain / for subdirectories (e.g. data/input.csv, assets/img/logo.png). Absolute paths (/etc/passwd) and .. traversal are rejected.
contentyes*The file body. UTF-8 text by default; base64-encoded bytes when encoding:"base64".
encodingno"utf8" (default — back-compatible with every existing text caller) or "base64" for arbitrary bytes (xlsx, parquet, images, zip, …).

* A FileInput carries exactly one of content (inline) or ref (a content-addressed blob). Use ref for large or reused files; use content for everything inline.

Backward compatibility

A request with no encoding and flat filenames behaves exactly as it always has: the content is written verbatim as UTF-8 text at /workspace/<name>. encoding is optional and defaults to utf8, so no existing caller changes.

Subdirectories

name may contain /. The worker creates the parent directories and writes the file at the relative path under /workspace. For example:

"files": [
  { "name": "main.py", "content": "import pandas as pd; print(pd.read_csv('data/input.csv').shape)" },
  { "name": "data/input.csv", "content": "a,b\n1,2\n3,4\n" }
]

materializes /workspace/main.py and /workspace/data/input.csv, and the program reads the CSV with the relative path it expects.

Binary files (base64)

Set encoding:"base64" and send the bytes base64-encoded. The worker decodes them and writes raw bytes:

"files": [
  { "name": "report.py", "content": "import openpyxl; wb = openpyxl.load_workbook('fixtures/sheet.xlsx'); print(wb.active.max_row)" },
  { "name": "fixtures/sheet.xlsx", "content": "UEsDBBQABgAIAAAAIQ…", "encoding": "base64" }
]

Node SDK: pass a Buffer, skip the base64

@teovilla/code-runner-sdk-node encodes binary inputs for you. Hand executeFiles a data field as a Buffer/Uint8Array and the SDK sets encoding:"base64" transparently; text files stay as { name, content }:

import { readFileSync } from 'node:fs';
import { CodeRunnerClient } from '@teovilla/code-runner-sdk-node';

const client = new CodeRunnerClient({ baseUrl, token });

await client.executeFiles({
  language: 'python',
  files: [
    { name: 'report.py', content: '…' },                                  // text → utf8
    { name: 'fixtures/sheet.xlsx', data: readFileSync('./sheet.xlsx') },  // binary → base64
  ],
});

executeFiles accepts a mix of raw wire FileInputs, text files ({ name, content }), and binary files ({ name, data }). The lower-level client.execute(req) still takes raw FileInputs unchanged if you prefer to encode yourself (or use the toFileInputs helper).

Size cap (MAX_FILES_BYTES)

The API sums the decoded size of all input files in a request and rejects it with 413 when the total exceeds MAX_FILES_BYTES (default 8 MiB). For a utf8 file the decoded size is its UTF-8 byte length; for a base64 file it is the decoded byte length (not the larger base64 string). Raise the cap for larger binary inputs:

MAX_FILES_BYTES=33554432   # 32 MiB

For files larger than this — or files reused across many runs — use content-addressed blobs instead of inlining.

Validation & safety

The API rejects bad requests up front:

ResultCause
400content is not valid base64 (when encoding:"base64").
400name is absolute (/…) or contains a .. traversal segment.
400A file has both content and ref, neither, or a malformed ref.
413Total decoded bytes exceed MAX_FILES_BYTES.

The worker re-sanitizes every path regardless of the API (host-escape-only threat model — the worker never trusts the path). Path cleaning is anchored at the workspace root, so a name can never escape /workspace. Subdirectories are preserved; absolute paths and .. traversal are refused.

Captured artifacts vs. input files

When collectOutput:true, the worker captures files the program wrote to the workspace as pullable artifacts. Input files are excluded from capture by their full relative path, so an input at data/input.csv is never echoed back as an artifact — only genuinely new outputs (e.g. out/plot.png) are.

Next

On this page