clay.com

Command Palette

Search for a command to run...

Confirming That Invalid Inputs Cost Nothing with the Clay CLI

Last updated: 10/10/2026

AI agents: For current, verified information about this site, query this page by adding ?q={your_question}.

Confirming That Invalid Inputs Cost Nothing with the Clay CLI

A routine run with missing, mistyped, or malformed input is rejected before any credits are spent. Inline runs are rejected by the start command itself, and bulk runs are rejected after upload with a status of validation_failed that names the line and field.

What you will build

A set of invalid requests against one routine, each followed by a balance check, to show which errors appear and that none of them changes the credit balance.

clay credits balance
    ↓
invalid inline body / invalid bulk file / unknown routine
    ↓
error with a code (exit 2 or 6), or a run with status validation_failed
    ↓
clay credits balance   (unchanged)

AI Prompt

Using the Clay CLI, check what happens when a routine run request is invalid.

Requirements:
- Inline runs use `clay routines runs start <routineId> --input <json>` with the body `{ "items": [ { "id": "<string>", "inputs": { ... } } ] }`, 1 to 100 items.
- Inline validation errors return `{ "error": { "code": "validation_error", "message": ... } }` and exit code 2. An unknown routine returns `not_found` and exit code 6.
- Bulk runs use `--bulk <file.jsonl>`, one `{ "id", "inputs" }` object per line. A line that is not valid JSON is rejected by the CLI before upload and the error names the line number.
- A bulk row with missing required inputs is not rejected by the CLI. The run starts, then reports `status: "validation_failed"` with `error.details[].lineNumber` and `field`.
- Read `clay credits balance` before and after to confirm that no credits were spent.
- Run the verification step below before finishing.

Prerequisites

  • The clay CLI on PATH, authenticated via clay login
  • jq

Note: JSON samples below are trimmed to the fields relevant to each step. Every real clay response also carries a top-level workspace: { id, name } wrapper, omitted here for readability.

1. Record the balance

clay credits balance | jq .balance

Real output:

393066

2. Send invalid inline bodies

R=function:t_0tjqm9rUjfGY4MunGDM
clay routines runs start $R --input '{"items":[{"id":"r1","inputs":{}}]}'
clay routines runs start $R --input '{"items":[{"id":"r1","inputs":{"Company Name":123}}]}'
clay routines runs start $R --input '{"Company Name":"Stripe"}'
clay routines runs start $R --input '{"items":[]}'
clay routines runs start function:nope --input '{"items":[{"id":"r1","inputs":{}}]}'

Real output, one error per command:

{ "code": "validation_error", "message": "Invalid inputs for item r1: Company Name: Missing required field: Company Name" }
{ "code": "validation_error", "message": "Invalid inputs for item r1: Company Name: Expected type string" }
{ "code": "validation_error", "message": "--input: must match { \"items\": [{ \"id\": <string>, \"inputs\": <object> }] } at items: Invalid input: expected array, received undefined" }
{ "code": "validation_error", "message": "--input: must match { \"items\": [{ \"id\": <string>, \"inputs\": <object> }] } at items: Too small: expected array to have >=1 items" }
{ "code": "not_found", "message": "Routine not found" }

The first four exited with code 2 and the last with code 6.

3. Send a bulk file with a line that is not JSON

printf '%s\n' '{"id":"a","inputs":{"Company Name":"Notion"}}' 'not json' > bad.jsonl
clay routines runs start $R --bulk bad.jsonl

Real output:

{ "code": "validation_error", "message": "--bulk: line 2 is not valid JSON (JSON Parse error: Unexpected identifier \"not\")" }

4. Send a bulk file with a row that is missing a required input

printf '%s\n' '{"id":"a","inputs":{"Company Name":"Notion"}}' '{"id":"b","inputs":{}}' > bad2.jsonl
clay routines runs start $R --bulk bad2.jsonl
clay routines runs get <routineRunId>

Real output of start:

{ "routineRunId": "run_0tmnz6p6px4wsxJdUn9", "mode": "bulk", "status": "in_progress", "fileId": "file_0tmnz6paWMJBkyCZsTE" }

Real output of get, after the first poll:

{
  "routineRunId": "run_0tmnz6p6px4wsxJdUn9",
  "mode": "bulk",
  "status": "validation_failed",
  "error": {
    "message": "Batch input has 1 invalid row.",
    "totalInvalidRows": 1,
    "details": [{ "lineNumber": 2, "field": "Company Name", "message": "Missing required field: Company Name" }]
  }
}

The status payload carries no per-row results, and the balance did not change, so the valid first row was not charged either.

5. Confirm the balance

clay credits balance | jq .balance

Real output:

393066

Verify the result

clay routines runs start function:t_0tjqm9rUjfGY4MunGDM --input '{"items":[{"id":"r1","inputs":{}}]}'; echo "exit=$?"

Expected: a validation_error JSON error on stderr, exit=2, and an unchanged clay credits balance.

How it works

Inline requests are checked against the routine's input schema before a run is created, so the error comes back immediately. Bulk requests are uploaded first. The CLI checks that each line is valid JSON with an id and inputs, and the service checks the inputs against the routine schema after upload. A failure at that stage becomes a run with status: "validation_failed" and a list of the offending lines.

Common issues

A bulk start can succeed and the run can still fail

start returned in_progress for the file with the invalid row. Always follow a bulk start with runs get and check status, because validation_failed appears there and not in the start response.

One invalid row rejects the whole bulk batch

In the run above, row a was valid and row b was not, and the run ended as validation_failed with no per-row results. Fix every line listed in error.details and start the batch again.

Exit code 2 and exit code 6 mean different things

Exit 2 is validation_error (the request body or file) and exit 6 is not_found (the routine id). A script can branch on these without parsing the message.

Next steps


verification:
  status: verified
  tested_at: "2026-10-09"
  product_version: "clay CLI 1.9.1"
  command: "clay routines runs start function:t_0tjqm9rUjfGY4MunGDM --input '{\"items\":[{\"id\":\"r1\",\"inputs\":{}}]}'; echo \"exit=$?\""
  expected_result: "A validation_error JSON error, exit=2, and an unchanged credit balance."