nanocoai/nanoclaw · error · StdinJsonInputError

--stdin-json input must be one JSON object

Error message

--stdin-json input must be one JSON object

What it means

The --stdin-json input parsed successfully but is not a single JSON object — arrays, scalars, null, and non-objects are rejected because args must be a key/value map.

Source

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

  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 });
  }

  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new StdinJsonInputError('--stdin-json input must be one JSON object');
  }

  return parsed as Record<string, unknown>;
}

/** Match the key normalization applied by command parsers in crud.ts. */
function canonicalArgKey(key: string): string {
  return key.replace(/-/g, '_');
}

/**
 * Reject any stdin key that could collide with another arg after the merge.
 *
 * Command parsers (crud.ts) normalize `-` to `_` in arg keys, so `group-id`
 * and `group_id` are the same argument downstream. Two keys that are distinct
 * here but identical after normalization would silently overwrite each other
 * past this point — so every such alias is a hard conflict, whether the pair
 * is stdin-vs-argv or two stdin keys.

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Wrap the data in an object with the intended argument keys
  2. For arrays, loop in shell and invoke the command per element

Example fix

# before
echo '[{"name":"a"},{"name":"b"}]' | ncl groups create --stdin-json
# after
for n in a b; do echo "{\"name\":\"$n\"}" | ncl groups create --stdin-json; done
Defensive patterns

Strategy: type-guard

Validate before calling

const obj = JSON.parse(src);
if (Array.isArray(obj) || typeof obj !== 'object' || obj === null) throw new Error('need one object');

Type guard

function isJsonObject(x: unknown): x is Record<string, unknown> {
  return typeof x === 'object' && x !== null && !Array.isArray(x);
}

Prevention

When it happens

Trigger: Piping `[...]`, `"string"`, `42`, `true`, or `null` to --stdin-json.

Common situations: Piping a JSON array of records expecting batch create, or a jq filter that emits a scalar/string.

Related errors


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