sipeed/picoclaw · error

Failed to encode response

Error message

Failed to encode response

What it means

HTTP 500 from GET /api/channels/{name}/config: json.NewEncoder(w).Encode(resp) failed while streaming the already-built response. By this point the response struct was constructed successfully, so an encoding failure almost always means the client went away mid-write (connection reset, navigation, proxy timeout). A marshaling failure is only possible if a code change introduced a non-JSON-serializable field into the response.

Source

Thrown at web/backend/api/channels.go:81

func (h *Handler) handleGetChannelConfig(w http.ResponseWriter, r *http.Request) {
	channelName := r.PathValue("name")
	item, ok := findChannelCatalogItem(channelName)
	if !ok {
		http.Error(w, "Channel not found", http.StatusNotFound)
		return
	}

	cfg, err := config.LoadConfig(h.configPath)
	if err != nil {
		http.Error(w, "Failed to load config", http.StatusInternalServerError)
		return
	}

	resp := buildChannelConfigResponse(cfg, item)

	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		http.Error(w, "Failed to encode response", http.StatusInternalServerError)
	}
}

func findChannelCatalogItem(name string) (channelCatalogItem, bool) {
	for _, item := range channelCatalog {
		if item.Name == name {
			return item, true
		}
	}
	return channelCatalogItem{}, false
}

var channelSecretFieldMap = map[string][]string{
	"weixin":          {"token"},
	"telegram":        {"token"},
	"discord":         {"token"},
	"slack":           {"bot_token", "app_token"},
	"feishu":          {"app_secret", "encrypt_key", "verification_token"},

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Correlate the timestamp with client/proxy logs — if the client aborted, it is benign noise
  2. Check proxy access logs for upstream resets/timeouts and raise idle timeouts if needed
  3. If reproducible with a stable client, inspect buildChannelConfigResponse output for non-marshalable types
  4. Consider marshal-then-write so transport and encoding failures are distinguishable
Defensive patterns

Strategy: try-catch

Validate before calling

// server-side hardening: marshal first so encode errors are distinguishable from transport errors
buf, err := json.Marshal(resp)
if err != nil {
    http.Error(w, "Failed to encode response", http.StatusInternalServerError)
    return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(buf)

Try / catch

Check the Encode error but classify it: after marshaling a plain struct, a write failure is a client disconnect — log at debug and move on; only a true marshal error (fixable struct) warrants a 500. Client-side, one cheap retry usually succeeds.

Prevention

When it happens

Trigger: Client cancels the fetch (AbortController, tab close) exactly as the response streams; an intermediary (proxy/ALB) times out or resets the upstream connection; a change to buildChannelConfigResponse added a value encoding/json cannot marshal.

Common situations: Frontends aborting in-flight config fetches during rapid navigation; aggressive proxy idle timeouts; response struct gaining chan/func fields after a refactor.

Related errors


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