siyuan-note/siyuan · error
the request body is empty or truncated (EOF)
Error message
the request body is empty or truncated (EOF)
What it means
The shared request-field decoder reads the body into a JSON object map. When json.Decode returns io.EOF the body was empty or cut short before any JSON value; the decoder substitutes this explicit message instead of the generic parse-failure text so callers can distinguish missing bodies from malformed ones.
Solutions
- Ensure the HTTP client actually serializes and attaches a JSON body to the POST.
- Log the raw request body length server-side or client-side to confirm it is non-empty.
- Check intermediate proxies/gateways for body-size limits or truncation.
- Handle the error explicitly on the client: retry once with a re-serialized payload if the body may have been dropped.
Example fix
// before
fetch("/api/block/checkBlockRef", {method: "POST"}) // no body
// after
fetch("/api/block/checkBlockRef", {method: "POST", body: JSON.stringify({ids: ["20240101120000-abcdefg"]})}) Defensive patterns
Strategy: validation
Validate before calling
if (!bodyString || bodyString.length === 0) { throw new Error("refusing to send empty request body"); } Try / catch
try {
await post(path, body);
} catch (e) {
if (String(e.message).includes("empty or truncated")) {
console.error("Body was empty/truncated; resending...");
await post(path, JSON.stringify(body)); // re-serialize and retry once
} else { throw e; }
} Prevention
- Always pass a serialized body to POST requests
- Check HTTP client options that can drop bodies (redirects on POST, GET downgrades)
- Verify no proxy/gateway strips or truncates bodies
- Log request body length before sending in debug mode
When it happens
Trigger: POSTing to any endpoint that goes through blockRequestFields (checkBlockRef, heading-level transactions, bazaar requests, AV-bound decoders) with a zero-length body, or a body stream that closes before emitting any bytes.
Common situations: HTTP clients forgetting to attach the JSON body, an upstream proxy/load balancer or gzip layer truncating the request, or read timeouts on slow uploads cutting the stream early.
Related errors
- Parses request [ ] failed
- the request body is empty or truncated (EOF)
- the request body is empty or truncated (EOF)
- asset path [ ] does not match data path [ ]
- box and dataPath cannot be used together
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/2ab219536f4ac740.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/block_remaining.go:59
}
// 特殊解码仅保留这些入口的条件参数和结构体绑定语义,响应仍受各自的契约约束。
func init() {
CheckBlockRef.decodeRequest = decodeCheckBlockRef
GetHeadingLevelTransaction.decodeRequest = decodeHeadingLevel
GetDocHeadingLevelTransaction.decodeRequest = func(reader io.Reader) (request DocHeadingLevelRequest, err error) {
if json.NewDecoder(reader).Decode(&request) != nil {
err = errors.New("invalid heading conversion parameters")
}
return
}
}
func blockRequestFields(reader io.Reader, path string) (fields map[string]json.RawMessage, err error) {
err = json.NewDecoder(reader).Decode(&fields)
if err != nil {
if errors.Is(err, io.EOF) {
err = errors.New("the request body is empty or truncated (EOF)")
}
err = fmt.Errorf("Parses request [%s] failed: %s", path, err)
}
return
}
func decodeCheckBlockRef(reader io.Reader) (request CheckBlockRefRequest, err error) {
fields, err := blockRequestFields(reader, "/api/block/checkBlockRef")
if err != nil {
return request, err
}
if raw, present := fields["scope"]; present {
if bytes.Equal(raw, []byte("null")) || json.Unmarshal(raw, &request.Scope) != nil {
return request, errors.New("Field [scope] should be of type [String]")
}
}
if strings.TrimSpace(request.Scope) == "" {
request.Scope = "blocks"View on GitHub (pinned to 9f775e8a12)