Sume 400: Idempotency-Key must be 255 printable characters or less

The Sume API rejects an Idempotency-Key over 255 characters or with control characters. Why it happens (JSON blobs, newlines) and a TypeScript key builder.

4 min readSume
All posts

The error Idempotency-Key must be 255 printable characters or less. is a 400 invalid_request from the Sume API. It has two causes: the key is longer than 255 characters, or it contains a control character such as a newline, a tab or a null byte. Both are caught before the job is created, so nothing is spent.

The rule

The jobs documentation gives the valid range as 1 to 255 characters. The API trims whitespace at both ends first and then checks the length and the control characters (the range from \u0000 to \u001f, plus \u007f). Interior spaces and normal punctuation are allowed.

Where long keys come from

  • A key made from the whole prompt text, which can run to thousands of characters.
  • A key made from a serialized request body, with newlines from pretty printing.
  • A key read from a file or a secret store, where a trailing line feed is common. Trimming removes that one, but a newline inside the value still fails.

The fix

Do not put content in the key. Put a digest of it there. A SHA-256 hex digest is 64 characters and has no control characters, so it is always valid. Prefix it with a short readable namespace if you want to find the job in a log.

Idempotency-Key checks in the Sume API (read 2026-10-04)
InputResult
No header, or blank after trimAccepted, no idempotency
1 to 255 printable charactersAccepted
More than 255 characters400 invalid_request
A control character inside400 invalid_request

TypeScript builder

It uses Web Crypto, which is available in Node 18 and later, Bun, Deno and Workers.

export async function idempotencyKey(namespace: string, work: unknown): Promise<string> {
  const text = `${namespace}\n${JSON.stringify(work)}`;
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
  const hex = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
  const key = `${namespace.slice(0, 40)}-${hex}`;
  if (key.length > 255 || /[\u0000-\u001f\u007f]/.test(key)) throw new Error("bad idempotency key");
  return key;
}

console.log((await idempotencyKey("batch-1", { sku: "A-100", prompt: "x".repeat(5000) })).length);  // 72

One more rule

A retry must reuse the exact same key. If you rebuild the key from a prompt that you trimmed or reformatted between attempts, the key changes and the replay protection is lost. Compute it once, store it with the job record, and send that stored value every time.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume