JuliusBrussee/caveman · error

json splice: invalid array range

Error message

json splice: invalid array range

What it means

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.

Solutions

  1. Verify body[span.Start]=='[' && body[span.End-1]==']' at the call site before appending
  2. Re-locate the array span on the current body bytes
  3. Branch on payload schema: use AppendObjectFields for object targets, AppendArrayElements only for real arrays
  4. Cache no spans across transforms; recompute after every mutation

Example fix

// before: object span passed to array append
out, err := jsonsplice.AppendArrayElements(body, objSpan, elem)
// after: locate the actual array
arrSpan, ok := findArraySpan(body, "system")
if !ok { return nil, fmt.Errorf("system is not an array") }
out, err := jsonsplice.AppendArrayElements(body, arrSpan, elem)
Defensive patterns

Strategy: validation

Validate before calling

func isArraySpan(body []byte, s jsonsplice.Span) bool {
    return s.Start >= 0 && s.End <= len(body) && s.Start < s.End && body[s.Start] == '[' && body[s.End-1] == ']'
}

Try / catch

if !isArraySpan(body, span) { return nil, fmt.Errorf("target span is not a JSON array") }
out, err := jsonsplice.AppendArrayElements(body, span, elems...)

Prevention

When it happens

Trigger: 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).

Common situations: 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 ']'.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/d075253050365ca5. Report an issue: GitHub.

Appendix: source

Thrown at proxy/providers/jsonsplice/jsonsplice.go:201

		}
		addition.Write(name)
		addition.WriteByte(':')
		addition.Write(field.Value)
	}

	out := make([]byte, 0, len(body)+addition.Len())
	out = append(out, body[:insertAt]...)
	out = append(out, addition.Bytes()...)
	out = append(out, body[insertAt:]...)
	return out, nil
}

// AppendArrayElements inserts elements immediately before an array's closing
// bracket. Existing bytes, whitespace, and element order remain untouched.
func AppendArrayElements(body []byte, array Span, elements ...[]byte) ([]byte, error) {
	if array.Start < 0 || array.End > len(body) || array.Start >= array.End ||
		body[array.Start] != '[' || body[array.End-1] != ']' {
		return nil, fmt.Errorf("json splice: invalid array range")
	}
	if len(elements) == 0 {
		return body, nil
	}

	insertAt := array.End - 1
	for insertAt > array.Start+1 && bytes.ContainsRune([]byte(" \n\r\t"), rune(body[insertAt-1])) {
		insertAt--
	}
	hasElements := insertAt > array.Start+1

	var addition bytes.Buffer
	if hasElements {
		addition.WriteByte(',')
	}
	for i, element := range elements {
		if !json.Valid(element) {
			return nil, fmt.Errorf("json splice: invalid element insertion")

View on GitHub (pinned to 3ee70a1026)