wavetermdev/waveterm · error

failed to parse JSON: %v

Error message

failed to parse JSON: %v

What it means

After reading the body, handleRender unmarshals it into rpctypes.VDomFrontendUpdate; invalid JSON returns HTTP 400 'failed to parse JSON: <err>' (tsunami/engine/serverhandlers.go:124). The embedded error names the exact field/type mismatch.

Source

Thrown at tsunami/engine/serverhandlers.go:124

		}
	}()

	setNoCacheHeaders(w)

	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to read request body: %v", err), http.StatusBadRequest)
		return
	}

	var feUpdate rpctypes.VDomFrontendUpdate
	if err := json.Unmarshal(body, &feUpdate); err != nil {
		http.Error(w, fmt.Sprintf("failed to parse JSON: %v", err), http.StatusBadRequest)
		return
	}

	if feUpdate.ForceTakeover {
		h.Client.clientTakeover(feUpdate.ClientId)
	}

	if err := h.Client.checkClientId(feUpdate.ClientId); err != nil {
		http.Error(w, fmt.Sprintf("client id error: %v", err), http.StatusBadRequest)
		return
	}

	startTime := time.Now()
	update, err := h.processFrontendUpdate(&feUpdate)
	duration := time.Since(startTime)

	if err != nil {
		http.Error(w, fmt.Sprintf("render error: %v", err), http.StatusInternalServerError)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read the wrapped json error to find the offending field/path and fix the payload structure.
  2. Validate the JSON locally (JSON.parse or a linter) before sending; ensure Content-Type is application/json.
  3. Marshal the request from the rpctypes.VDomFrontendUpdate type (or its TS equivalent) instead of hand-building the object.
  4. Check for client/server version mismatch where the VDomFrontendUpdate schema changed; upgrade both sides.
  5. If sending gzip, declare Content-Encoding so the server receives decoded bytes.

Example fix

// before: invalid payload
await fetch(renderUrl, {method: "POST", body: "{ nodeId: 1 }"}) // unquoted keys

// after
await fetch(renderUrl, {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({nodeId: 1, ...feUpdate})})
Defensive patterns

Strategy: validation

Validate before calling

const payload = JSON.stringify(feUpdate);
JSON.parse(payload); // throws locally before the network call if invalid
const resp = await fetch(renderUrl, {method: "POST", headers: {"Content-Type": "application/json"}, body: payload});

Type guard

function isFeUpdate(u) {
  return u != null && typeof u === "object" && typeof u.ClientId === "string";
}

Try / catch

const resp = await fetch(renderUrl, {method: "POST", body: payload});
if (resp.status === 400) {
  const msg = await resp.text();
  if (msg.includes("failed to parse JSON")) throw new Error(`schema mismatch with VDomFrontendUpdate: ${msg}`);
}

Prevention

When it happens

Trigger: Posting a body that is not valid JSON, sending the wrong content type/encoding, or a payload whose fields have types that don't match VDomFrontendUpdate (e.g. a string where a number is expected, wrong JSON casing).

Common situations: Hand-crafted curl bodies with unquoted keys or trailing commas, clients sending form-encoded or compressed data without declaring it, SDK version drift so the client emits fields no longer matching rpctypes.VDomFrontendUpdate.

Understand the failure class

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/c877a46fccaded05. Report an issue: GitHub.