nanocoai/nanoclaw · error · StdinJsonInputError

--stdin-json input exceeds ${MAX_STDIN_JSON_BYTES} bytes

Error message

--stdin-json input exceeds ${MAX_STDIN_JSON_BYTES} bytes

What it means

readBounded streams stdin for --stdin-json and enforces MAX_STDIN_JSON_BYTES; exceeding the cap aborts before parsing to bound memory use.

Source

Thrown at src/cli/stdin-json.ts:54

 * therefore normalized to a Buffer and the running total counts
 * `buffer.byteLength` — the true encoded size — rather than string length,
 * where a multibyte character (e.g. "ש", 2 bytes) would count as 1.
 *
 * Decoding to text happens exactly once, after the whole input is collected:
 * a chunk boundary can fall in the middle of a multibyte character, so
 * decoding chunk-by-chunk could corrupt the character that straddles the
 * split. The limit check runs before buffering grows past the cap, so an
 * oversized (or unbounded) pipe is rejected without being read to the end.
 */
async function readBounded(stream: StdinJsonStream): Promise<string> {
  const chunks: Buffer[] = [];
  let byteLength = 0;

  for await (const chunk of stream) {
    const buffer = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : Buffer.from(chunk);
    byteLength += buffer.byteLength;
    if (byteLength > MAX_STDIN_JSON_BYTES) {
      throw new StdinJsonInputError(`--stdin-json input exceeds ${MAX_STDIN_JSON_BYTES} bytes`);
    }
    chunks.push(buffer);
  }

  return Buffer.concat(chunks, byteLength).toString('utf8');
}

/** Parse the input, requiring exactly one JSON object — not an array, scalar, or null. */
function parseJsonObject(source: string): Record<string, unknown> {
  if (source.trim().length === 0) {
    throw new StdinJsonInputError('--stdin-json input is empty');
  }

  let parsed: unknown;
  try {
    parsed = JSON.parse(source);
  } catch (err) {
    throw new StdinJsonInputError('--stdin-json input is not valid JSON', { cause: err });

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Shrink the payload — remove large blobs or move big data to a file path the command references instead
  2. Split into multiple smaller operations
  3. Pipe the correct, intended JSON file

Example fix

# before
cat huge-payload.json | ncl groups update --id g1 --stdin-json
# after
ncl groups update --id g1 --name newname
Defensive patterns

Strategy: validation

Validate before calling

const size = fs.statSync(path).size;
if (size > MAX_STDIN_JSON_BYTES) throw new Error('payload too large; move data to a file path');

Prevention

When it happens

Trigger: Piping a JSON document larger than MAX_STDIN_JSON_BYTES to `ncl ... --stdin-json` (e.g. a huge embedded payload, base64 blob, or accidental binary file).

Common situations: Embedding large assets in a create/update payload, or accidentally piping a log/CSV instead of the intended small object.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/f0359b2e59d4172a. Report an issue: GitHub.