{"record":{"id":"4bc44eb5725a95b5","repo":"larksuite/cli","slug":"trailing-data-after-json-value","errorCode":null,"errorMessage":"trailing data after JSON value","messagePattern":"trailing data after JSON value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/sheets/lark_sheet_table_io.go","lineNumber":326,"sourceCode":"\t\treturn \"float64\"\n\tcase \"date\":\n\t\treturn \"datetime64[ns]\"\n\tcase \"bool\":\n\t\treturn \"bool\"\n\tdefault:\n\t\treturn \"object\"\n\t}\n}\n\n// decoderExpectEOF ensures the decoder has nothing left to read after a\n// successful Decode. json.Decoder accepts trailing non-whitespace after the\n// first JSON value (unlike json.Unmarshal), so a payload like `{...} trailing`\n// would silently be treated as the leading object only. Use this after the\n// first Decode to surface the trailing data as a validation error.\nfunc decoderExpectEOF(dec *json.Decoder) error {\n\tvar trailing json.RawMessage\n\tif err := dec.Decode(&trailing); err == nil {\n\t\treturn fmt.Errorf(\"trailing data after JSON value\") //nolint:forbidigo // intermediate error; the caller wraps it into a typed --sheets/--values validation error\n\t} else if !errors.Is(err, io.EOF) {\n\t\treturn fmt.Errorf(\"trailing data after JSON value: %w\", err) //nolint:forbidigo // intermediate error; the caller wraps it into a typed --sheets/--values validation error\n\t}\n\treturn nil\n}\n\n// tablePutSheetsSkeleton is the one-line --sheets shape inlined on a decode\n// error, so the retry needs no --print-schema round trip. Field vocabulary\n// mirrors tableSheetIn.\nconst tablePutSheetsSkeleton = `{\"sheets\":[{\"name\":\"Sheet1\",\"columns\":[\"City\",\"Revenue\"],\"dtypes\":{\"Revenue\":\"float64\"},\"data\":[[\"SH\",123.4],[\"BJ\",56.7]],\"start_cell\":\"A1\"}]}`\n\n// parseTablePutPayload reads --sheets (JSON, supports @file / stdin) into a\n// validated payload. UseNumber keeps numeric cells as json.Number so large\n// integers (order IDs, etc.) survive without precision loss or scientific\n// notation. The wire shape (tableSheetIn: string columns + dtypes/formats maps\n// + `data`) is normalized into the internal tableSheetSpec so the rest of the\n// file (buildSheetMatrix, sheetCreateDims, …) is unaware of it. Network-free:\n// safe from Validate and DryRun.","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/sheets/lark_sheet_table_io.go#L308-L344","documentation":"decoderExpectEOF validates that a JSON payload (--sheets/--values) contains exactly one JSON value. After decoding the first value it tries another Decode; if that succeeds, trailing data existed after the value, so this plain error is thrown. It prevents payloads like `{...} garbage` from being silently truncated to the leading object.","triggerScenarios":"Passing a --sheets/--values argument with extra characters after the closing brace/bracket, e.g. '{\"columns\":[]} extra' or two concatenated JSON objects '{..}{..}', or pasted content with a trailing comment.","commonSituations":"Pasting JSON from docs with a trailing sentence; concatenating two JSON blobs by mistake; shell appending an unquoted fragment; a stray comma-plus-object tail.","solutions":["Remove everything after the final closing } or ] of the JSON value","If you need multiple objects, wrap them in one array or one object instead of concatenating","Re-quote the whole payload so the shell passes it as a single argument"],"exampleFix":"// before\n--sheets '{\"columns\":[...]} {\"extra\":1}'\n// after\n--sheets '{\"columns\":[...]}'","handlingStrategy":"validation","validationCode":"function assertSingleJSONValue(s) {\n  const v = JSON.parse(s);\n  const trimmed = s.trim();\n  // re-serialize round-trip guards against trailing bytes after the value\n  const probe = trimmed.replace(/^\\s*[[{]/, m => m);\n  JSON.parse(s + '');// syntax check\n  if (countTopLevel(s) !== 1) throw new Error('trailing data after JSON value');\n  return v;\n}\nfunction countTopLevel(s) { let d=0,n=0,inStr=false,esc=false; for(const c of s){ if(esc){esc=false;continue;} if(c==='\\\\'){esc=true;continue;} if(c==='\"'){inStr=!inStr;continue;} if(inStr)continue; if(c==='{'||c==='[')d++; if(c==='}'||c===']'){d--; if(d===0)n++;}} return n; }","typeGuard":"function isCleanJSONObject(s) { try { JSON.parse(s); return /^\\s*[\\[{]/.test(s) && /\\s*[\\]}]\\s*$/.test(s); } catch { return false; } }","tryCatchPattern":"try {\n  await run(['lark','sheet','table-put','--sheets',payload]);\n} catch (e) {\n  if (/trailing data after JSON value/.test(e.message)) {\n    payload = JSON.stringify(JSON.parse(payload.trim().replace(/^[\\[{]/,'')));\n    // simplest: rebuild payload via JSON.stringify from the parsed object\n  }\n  throw e;\n}","preventionTips":["Build payloads with JSON.stringify instead of hand-concatenating strings","Never paste JSON together with explanatory text or comments","Pipe payloads from jq (jq -c .) which guarantees one valid JSON value per output"],"tags":["cli","json","validation"],"backgroundTag":"trailing-data-after-json","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}