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
- Verify the located span actually brackets an object (first byte '{', last byte '}') before calling
- Re-locate the object span on the exact body instance passed in
- Confirm the field target exists in the current provider payload schema (e.g. system is an object here, array elsewhere)
- 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
- Confirm the target field is an object in the current provider schema before appending fields
- Re-locate the span on the live body, not a cached one
- Verify locator output lands on '{' and '}' in tests
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
- json splice: invalid array range
- json splice: invalid range
- json splice: replacements for candidates
- json splice: invalid element insertion
- json splice: invalid field insertion
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)