{"record":{"id":"d075253050365ca5","repo":"JuliusBrussee/caveman","slug":"json-splice-invalid-array-range","errorCode":null,"errorMessage":"json splice: invalid array range","messagePattern":"json splice: invalid array range","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/jsonsplice/jsonsplice.go","lineNumber":201,"sourceCode":"\t\t}\n\t\taddition.Write(name)\n\t\taddition.WriteByte(':')\n\t\taddition.Write(field.Value)\n\t}\n\n\tout := make([]byte, 0, len(body)+addition.Len())\n\tout = append(out, body[:insertAt]...)\n\tout = append(out, addition.Bytes()...)\n\tout = append(out, body[insertAt:]...)\n\treturn out, nil\n}\n\n// AppendArrayElements inserts elements immediately before an array's closing\n// bracket. Existing bytes, whitespace, and element order remain untouched.\nfunc AppendArrayElements(body []byte, array Span, elements ...[]byte) ([]byte, error) {\n\tif array.Start < 0 || array.End > len(body) || array.Start >= array.End ||\n\t\tbody[array.Start] != '[' || body[array.End-1] != ']' {\n\t\treturn nil, fmt.Errorf(\"json splice: invalid array range\")\n\t}\n\tif len(elements) == 0 {\n\t\treturn body, nil\n\t}\n\n\tinsertAt := array.End - 1\n\tfor insertAt > array.Start+1 && bytes.ContainsRune([]byte(\" \\n\\r\\t\"), rune(body[insertAt-1])) {\n\t\tinsertAt--\n\t}\n\thasElements := insertAt > array.Start+1\n\n\tvar addition bytes.Buffer\n\tif hasElements {\n\t\taddition.WriteByte(',')\n\t}\n\tfor i, element := range elements {\n\t\tif !json.Valid(element) {\n\t\t\treturn nil, fmt.Errorf(\"json splice: invalid element insertion\")","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/jsonsplice/jsonsplice.go#L183-L219","documentation":"AppendArrayElements splices elements before an array's closing bracket and validates that the span is in-bounds, non-empty, and actually brackets an array (starts '[' and ends ']'). Anything else aborts to keep the document byte-exact.","triggerScenarios":"injectConverseCachePointRaw given a span on a non-array (e.g. the 'system' object or a string field); stale span after re-serialization; bounds violation from End > len(body).","commonSituations":"Targeting a field that is an object in one provider schema and an array in another; span computed on a template body differing from the live one; locator off-by-one grabbing the last element instead of ']'.","solutions":["Verify body[span.Start]=='[' && body[span.End-1]==']' at the call site before appending","Re-locate the array span on the current body bytes","Branch on payload schema: use AppendObjectFields for object targets, AppendArrayElements only for real arrays","Cache no spans across transforms; recompute after every mutation"],"exampleFix":"// before: object span passed to array append\nout, err := jsonsplice.AppendArrayElements(body, objSpan, elem)\n// after: locate the actual array\narrSpan, ok := findArraySpan(body, \"system\")\nif !ok { return nil, fmt.Errorf(\"system is not an array\") }\nout, err := jsonsplice.AppendArrayElements(body, arrSpan, elem)","handlingStrategy":"validation","validationCode":"func isArraySpan(body []byte, s jsonsplice.Span) bool {\n    return s.Start >= 0 && s.End <= len(body) && s.Start < s.End && body[s.Start] == '[' && body[s.End-1] == ']'\n}","typeGuard":null,"tryCatchPattern":"if !isArraySpan(body, span) { return nil, fmt.Errorf(\"target span is not a JSON array\") }\nout, err := jsonsplice.AppendArrayElements(body, span, elems...)","preventionTips":["Branch between AppendObjectFields and AppendArrayElements based on the actual decoded type","Relocate the span on the current body before appending","Assert locator endpoints on '[' and ']' in tests"],"tags":["json","go","byte-splice","offsets"],"backgroundTag":"argument-out-of-range","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}