sipeed/picoclaw · warning

Invalid JSON: %v

Error message

Invalid JSON: %v

What it means

Returned (HTTP 400) by PUT /api/system/autostart in web/backend/api/startup.go:57-59 when json.NewDecoder(r.Body).Decode(&req) fails. The handler decodes the request body into autoStartRequest, a struct with a single boolean field `enabled`; any byte stream that is not a valid JSON object with a boolean-compatible `enabled` value is rejected. The handler does not check Content-Type, so even a form-encoded or text body reaches the decoder and fails. The %v part carries Go's encoding/json error detail (e.g. EOF, invalid character, cannot unmarshal string into bool).

Source

Thrown at web/backend/api/startup.go:58

	enabled, supported, message, err := h.getAutoStartStatus()
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to read startup setting: %v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(autoStartResponse{
		Enabled:   enabled,
		Supported: supported,
		Platform:  runtime.GOOS,
		Message:   message,
	})
}

func (h *Handler) handleSetAutoStart(w http.ResponseWriter, r *http.Request) {
	var req autoStartRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
		return
	}

	if err := h.setAutoStart(req.Enabled); err != nil {
		if errors.Is(err, errAutoStartUnsupported) {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		http.Error(w, fmt.Sprintf("Failed to update startup setting: %v", err), http.StatusInternalServerError)
		return
	}

	enabled, supported, message, err := h.getAutoStartStatus()
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to verify startup setting: %v", err), http.StatusInternalServerError)
		return
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Send a JSON object with a boolean: curl -X PUT -H 'Content-Type: application/json' -d '{"enabled": true}' http://host/api/system/autostart
  2. Ensure the value is a real boolean, not the string "true" — coerce with typeof check before fetch
  3. Set Content-Type: application/json and confirm the body is not empty or double-encoded
  4. If proxying, verify the request body survives the hop (no body-stripping redirects such as 307/308 mishandling)

Example fix

// before
await fetch('/api/system/autostart', { method: 'PUT', body: 'enabled=true' });
// after
await fetch('/api/system/autostart', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ enabled: true }),
});
Defensive patterns

Strategy: validation

Validate before calling

// Run before PUT /api/system/autostart
function assertAutoStartPayload(body: unknown): { enabled: boolean } {
  if (typeof body !== 'object' || body === null || Array.isArray(body)) throw new TypeError('body must be an object');
  const { enabled } = body as { enabled?: unknown };
  if (typeof enabled !== 'boolean') throw new TypeError('enabled must be a boolean');
  return { enabled };
}
await fetch('/api/system/autostart', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(assertAutoStartPayload(state)),
});

Type guard

const isAutoStartRequest = (v: unknown): v is { enabled: boolean } =>
  typeof v === 'object' && v !== null &&
  typeof (v as any).enabled === 'boolean';

Prevention

When it happens

Trigger: PUT /api/system/autostart with: an empty request body (decode returns EOF); truncated or malformed JSON like {"enabled": tru}; sending "enabled":"true" (string instead of bool); a UTF-8 BOM prefix; a JSON array body; or a proxy that strips the body.

Common situations: Frontend sends the toggle state as form data or query string instead of a JSON body; a curl call missing -d entirely; string-typed booleans coming from a config UI state store; double JSON.stringify causing a quoted body.

Understand the failure class

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/4e7d1bd8b351fcc8. Report an issue: GitHub.