JuliusBrussee/caveman · error

json splice: invalid object range

Error message

json splice: invalid object range

What it means

AppendObjectFields splices fields just before a JSON object's closing brace and validates that the span bounds the whole body, starts with '{', and ends with '}'. Any violation (bad bounds or the span is not actually an object) aborts to preserve byte integrity.

Solutions

  1. Verify the located span actually brackets an object (first byte '{', last byte '}') before calling
  2. Re-locate the object span on the exact body instance passed in
  3. Confirm the field target exists in the current provider payload schema (e.g. system is an object here, array elsewhere)
  4. Log body[span.Start] and body[span.End-1] on failure to spot the off-by-one

Example fix

// before: span points at a string value
span := findSpan(body, "model") // "claude-3" value span
out, err := jsonsplice.AppendObjectFields(body, span, field)
// after: span must bracket the whole object
objSpan, ok := findObjectSpan(body, "system")
if !ok || body[objSpan.Start] != '{' { return nil, fmt.Errorf("system is not an object") }
out, err := jsonsplice.AppendObjectFields(body, objSpan, field)
Defensive patterns

Strategy: validation

Validate before calling

func isObjectSpan(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 !isObjectSpan(body, span) { return nil, fmt.Errorf("target span is not a JSON object") }
out, err := jsonsplice.AppendObjectFields(body, span, fields...)

Prevention

When it happens

Trigger: inject*Raw helpers given a span pointing at a value that is not an object; span computed against a different body; End-1 landing on whitespace or ']' due to an off-by-one in the locator.

Common situations: Targeting a top-level 'system' that is a string instead of an array/object; span cached from a prior request shape; a locator that returns the last byte of the object's final value instead of the closing brace.

Related errors


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

Appendix: source

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

		return nil, fmt.Errorf("json splice: invalid range")
	}
	if !json.Valid(replacement) {
		return nil, fmt.Errorf("json splice: replacement is not valid JSON")
	}
	out := make([]byte, 0, len(body)-(span.End-span.Start)+len(replacement))
	out = append(out, body[:span.Start]...)
	out = append(out, replacement...)
	out = append(out, body[span.End:]...)
	return out, nil
}

// AppendObjectFields inserts fields immediately before an object's closing
// brace. Existing bytes, whitespace, key order, and escapes remain untouched.
// Callers must reject existing decoded keys before calling this function.
func AppendObjectFields(body []byte, object Span, fields ...FieldInsertion) ([]byte, error) {
	if object.Start < 0 || object.End > len(body) || object.Start >= object.End ||
		body[object.Start] != '{' || body[object.End-1] != '}' {
		return nil, fmt.Errorf("json splice: invalid object range")
	}
	if len(fields) == 0 {
		return body, nil
	}

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

	var addition bytes.Buffer
	if hasFields {
		addition.WriteByte(',')
	}
	for i, field := range fields {
		if field.Name == "" || !json.Valid(field.Value) {
			return nil, fmt.Errorf("json splice: invalid field insertion")

View on GitHub (pinned to 3ee70a1026)