plandex-ai/plandex · error

Error marshalling response

Error message

Error marshalling response

What it means

AutoLoadContextHandler builds a markdown response struct and serializes it with json.Marshal before writing it to the HTTP response. If marshaling fails (e.g., an unsupported type like a channel, func, or a cyclic data structure inside the response), it logs the underlying error and returns a 500 with the plain-text body 'Error marshalling response'. This indicates a server-side bug in response construction, not a client problem.

Source

Thrown at app/server/handlers/plans_exec.go:518

			branchName: branch,
			autoLoaded: true,
		})
	}

	if res == nil {
		// the client will treat this as a no-op
		markdownRes := shared.LoadContextResponse{
			TokensAdded:       0,
			TotalTokens:       0,
			MaxTokensExceeded: false,
			MaxTokens:         0,
			Msg:               "",
		}

		bytes, err := json.Marshal(markdownRes)
		if err != nil {
			log.Printf("Error marshalling response: %v\n", err)
			http.Error(w, "Error marshalling response", http.StatusInternalServerError)
			return
		}

		w.Write(bytes)
		return
	}

	log.Println("AutoLoadContextHandler - updating active plan")

	modelPlan.UpdateActivePlan(planId, branch, func(activePlan *types.ActivePlan) {
		if activePlan == nil {
			log.Println("Active plan is nil")
			http.Error(w, "Active plan is nil", http.StatusInternalServerError)
			return
		}
		activePlan.Contexts = append(activePlan.Contexts, dbContexts...)
		for _, dbContext := range dbContexts {
			activePlan.ContextsByPath[dbContext.FilePath] = dbContext

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the server log line 'Error marshalling response: <err>' to identify the offending field and type.
  2. Remove or replace non-serializable fields (chan/func/cyclic pointers) in the response struct with serializable equivalents (ids, copied data).
  3. Assign json:"-" to internal fields that should never be serialized.
  4. Add a unit test that marshals the response struct with representative data to catch regressions.

Example fix

// before
bytes, err := json.Marshal(markdownRes) // fails: markdownRes.Plan has cyclic pointer
// after
type markdownResponse struct {
    PlanID string `json:"planId"`
    Content string `json:"content"`
    // cyclic/internal fields removed or tagged `json:"-"`
}
bytes, err := json.Marshal(markdownRes)
Defensive patterns

Strategy: try-catch

Validate before calling

// Go client: nothing to pre-validate; ensure server response struct is marshal-tested
func TestMarkdownResponseMarshalable(t *testing.T) {
    if _, err := json.Marshal(shared.MarkdownResponse{}); err != nil {
        t.Fatalf("response not marshalable: %v", err)
    }
}

Type guard

func isMarshalable(v any) bool { _, err := json.Marshal(v); return err == nil }

Try / catch

resp, err := http.Post(url, "application/json", body)
if err != nil { return err }
if resp.StatusCode == http.StatusInternalServerError {
    b, _ := io.ReadAll(resp.Body)
    if strings.Contains(string(b), "Error marshalling response") {
        return fmt.Errorf("server serialization bug (1030): %s", b)
    }
}

Prevention

When it happens

Trigger: The markdownRes struct built by AutoLoadContextHandler contains a value json.Marshal cannot encode (chan, func, complex number, or a cyclic reference), so json.Marshal returns an error at plans_exec.go:518.

Common situations: A developer adds a field to the response struct whose type is not JSON-serializable, or populates a map/pointer chain that creates a cycle (e.g., a plan referencing itself). Also seen after upgrading a dependency field to a custom type without MarshalJSON.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/f488fe64acb840c6. Report an issue: GitHub.