{"record":{"id":"f3c3b4a38657f4a3","repo":"nanocoai/nanoclaw","slug":"stdin-json-input-must-be-one-json-object","errorCode":null,"errorMessage":"--stdin-json input must be one JSON object","messagePattern":"--stdin-json input must be one JSON object","errorType":"validation","errorClass":"StdinJsonInputError","httpStatus":null,"severity":"error","filePath":"container/agent-runner/src/cli/stdin-json.ts","lineNumber":76,"sourceCode":"\n  return Buffer.concat(chunks, byteLength).toString('utf8');\n}\n\n/** Parse the input, requiring exactly one JSON object — not an array, scalar, or null. */\nfunction parseJsonObject(source: string): Record<string, unknown> {\n  if (source.trim().length === 0) {\n    throw new StdinJsonInputError('--stdin-json input is empty');\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(source);\n  } catch (err) {\n    throw new StdinJsonInputError('--stdin-json input is not valid JSON', { cause: err });\n  }\n\n  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n    throw new StdinJsonInputError('--stdin-json input must be one JSON object');\n  }\n\n  return parsed as Record<string, unknown>;\n}\n\n/** Match the key normalization applied by command parsers in crud.ts. */\nfunction canonicalArgKey(key: string): string {\n  return key.replace(/-/g, '_');\n}\n\n/**\n * Reject any stdin key that could collide with another arg after the merge.\n *\n * Command parsers (crud.ts) normalize `-` to `_` in arg keys, so `group-id`\n * and `group_id` are the same argument downstream. Two keys that are distinct\n * here but identical after normalization would silently overwrite each other\n * past this point — so every such alias is a hard conflict, whether the pair\n * is stdin-vs-argv or two stdin keys.","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/container/agent-runner/src/cli/stdin-json.ts#L58-L94","documentation":"Second guard in parseMemoryMb: the value matched the size grammar but the numeric part is not finite (Number(match[1]) is NaN/Infinity). Practically hard to reach via the regex, but it keeps the parse total function and still fails closed rather than dropping the operator's memory cap.","triggerScenarios":"composeSessionSpec() parses a CONTAINER_MEMORY_LIMIT whose numeric component converts to a non-finite Number — e.g. an astronomically long digit string that overflows to Infinity.","commonSituations":"Script-generated env values, a pasted number with hundreds of digits, or edge-case fuzzing of the env var.","solutions":["Use a realistic finite value like 8g or 4096m","If you need 'no cap', set CONTAINER_MEMORY_LIMIT=0","Audit .env generation scripts that compute the limit"],"exampleFix":"# before\nCONTAINER_MEMORY_LIMIT=999999999999999999999999999999g\n\n# after\nCONTAINER_MEMORY_LIMIT=8g","handlingStrategy":"validation","validationCode":"const raw = process.env.CONTAINER_MEMORY_LIMIT;\nif (raw) {\n  const m = /^(\\d+(?:\\.\\d+)?)\\s*([bkmg]?)b?$/i.exec(raw.trim());\n  if (!m || !Number.isFinite(Number(m[1]))) throw new Error('bad CONTAINER_MEMORY_LIMIT');\n}","typeGuard":"function isFiniteDockerSize(v: string): boolean {\n  const m = /^(\\d+(?:\\.\\d+)?)\\s*([bkmg]?)b?$/i.exec(v.trim());\n  return !!m && Number.isFinite(Number(m[1]));\n}","tryCatchPattern":null,"preventionTips":["Don't script-generate memory limits without clamping magnitude","Prefer short human-scale values (8g, 4096m)"],"tags":["docker","memory-limit","env-var","number-overflow"],"backgroundTag":"invalid-env-var-value","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}