JuliusBrussee/caveman · error
json splice: replacement is not valid JSON
Error message
json splice: replacement is not valid JSON
What it means
ReplaceRaw requires the replacement bytes to already be valid JSON since they are inserted verbatim into the document. If json.Valid(replacement) is false the splice is aborted to avoid corrupting the envelope.
Solutions
- Run replacement through json.Marshal (or json.Valid in a unit test) before calling ReplaceRaw
- Quote raw strings with json.Marshal(string) so they become valid JSON literals
- Check the error of any earlier marshal step instead of ignoring it and passing empty/partial bytes
Example fix
// before: plain string spliced in
repl := []byte(`cache_control`)
out, err := jsonsplice.ReplaceRaw(body, span, repl)
// after: marshal to valid JSON
raw, err := json.Marshal(map[string]any{"cache_control": map[string]string{"type": "ephemeral"}})
if err != nil { return nil, err }
out, err := jsonsplice.ReplaceRaw(body, span, raw) Defensive patterns
Strategy: validation
Validate before calling
if !json.Valid(repl) { return fmt.Errorf("replacement is not valid JSON: %q", repl) } Try / catch
raw, err := json.Marshal(value)
if err != nil { return nil, err }
if !json.Valid(raw) { return nil, fmt.Errorf("marshal produced invalid JSON") }
out, err := jsonsplice.ReplaceRaw(body, span, raw) Prevention
- Always produce replacements via json.Marshal, never string concatenation
- Quote bare strings with json.Marshal(string)
- Check every upstream marshal error; never pass partial bytes through
When it happens
Trigger: Passing Go values, plain strings, or partially-serialized buffers as replacement instead of marshaled JSON; hand-built JSON with a syntax error; forgetting json.Marshal on a string value (must be quoted).
Common situations: Injecting a cache_control object where the struct was serialized with a non-JSON encoder; interpolating Go strings directly into the replacement; truncated JSON from a failed marshal earlier in the pipeline.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- json splice: invalid element insertion
- json splice: invalid field insertion
- json splice: replacements for candidates
- json splice: invalid array range
- json splice: invalid object range
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/283aff289300f8ac.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/providers/jsonsplice/jsonsplice.go:142
out = append(out, body[last:candidate.Start]...)
out = append(out, quoted...)
last = candidate.End
}
if out == nil {
return body, nil
}
return append(out, body[last:]...), nil
}
// ReplaceRaw replaces one JSON value while preserving every byte outside span.
// replacement must itself be valid JSON. Unlike Replace, it does not quote the
// replacement and is suitable for provider-envelope edits.
func ReplaceRaw(body []byte, span Span, replacement []byte) ([]byte, error) {
if span.Start < 0 || span.End > len(body) || span.Start >= span.End {
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, nilView on GitHub (pinned to 3ee70a1026)