Run Metadata
Attach up to 256 KB of structured data to a run. Access it inside the task, via API, Realtime subscriptions, or the dashboard.
Setting Metadata
At Trigger Time
ts
const handle = await myTask.trigger(
{ message: "hello world" },
{ metadata: { user: { name: "Eric", id: "user_1234" } } }
);Inside the Run
ts
import { task, metadata } from "@trigger.dev/sdk";
export const myTask = task({
id: "my-task",
run: async (payload: { message: string }) => {
const currentMetadata = metadata.current(); // entire object
const user = metadata.get("user"); // specific key
console.log(user.name); // "Eric"
},
});Updates API
All update methods are synchronous and non-blocking — the SDK flushes to the database in the background.
ts
metadata.set("progress", 0.5) // Set a key
metadata.del("progress") // Delete a key
metadata.replace({ newObj: true }) // Replace entire metadata object
metadata.append("logs", "Step 1") // Append item to array
metadata.remove("logs", "Step 1") // Remove item from array
metadata.increment("progress", 0.4) // Increment a number
metadata.decrement("progress", 0.1) // Decrement a number
await metadata.flush() // Force immediate persist to DBFluent (Chainable) API
ts
metadata
.set("progress", 0.1)
.append("logs", "Step 1 complete")
.increment("progress", 0.4)
.decrement("otherProgress", 0.1);Parent & Root Updates
Child tasks can update metadata of parent or root tasks:
ts
export const childTask = task({
id: "child-task",
run: async (payload) => {
metadata.parent.set("progress", 0.5); // update parent task's metadata
metadata.root.set("status", "running"); // update root task's metadata
},
});All update methods (set, del, append, etc.) are available on metadata.parent and metadata.root.
Real-World Example: CSV Processing with Progress
ts
export const handleCSVRow = schemaTask({
id: "handle-csv-row",
schema: CSVRow,
run: async (row, { ctx }) => {
// Report progress back to parent
metadata.parent
.increment("processedRows", 1)
.append("rowRuns", ctx.run.id);
return row;
},
});
export const handleCSVUpload = schemaTask({
id: "handle-csv-upload",
schema: UploadedFileData,
run: async (file) => {
metadata.set("status", "fetching");
const rows = await parseCSVFromUrl(file.url);
metadata.set("status", "processing").set("totalRows", rows.length);
const results = await batch.triggerAndWait(
rows.map(row => ({ id: "handle-csv-row", payload: row }))
);
metadata.set("status", "complete");
return { file, rows, results };
},
});Common Patterns
Progress Tracking
ts
metadata.set("progress", {
step: i + 1,
total: payload.records.length,
percentage: Math.round(((i + 1) / payload.records.length) * 100),
currentRecord: record.id,
});Status + Log Stream
ts
metadata
.set("status", "building")
.append("logs", "Building application...");Type Safety with Zod
ts
import { z } from "zod";
const Metadata = z.object({
user: z.object({ name: z.string(), id: z.string() }),
date: z.coerce.date(), // coerce string back to Date when reading
});
function getMetadata() {
return Metadata.parse(metadata.current());
}Constraints
- Cannot use top-level arrays or strings as metadata — must be an object
- No functions or class instances
- Dates serialize to ISO strings — use
z.coerce.date()when reading back
Inspecting Metadata
- Dashboard: Run details view shows current metadata
- API:
runs.retrieve("run_1234")→run.metadata
Size Limit
Max 256 KB. Self-hosting: increase via TASK_RUN_METADATA_MAXIMUM_SIZE env var.