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

  1. Ensure the HTTP client actually serializes and attaches a JSON body to the POST.
  2. Log the raw request body length server-side or client-side to confirm it is non-empty.
  3. Check intermediate proxies/gateways for body-size limits or truncation.
  4. 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

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


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)