plandex-ai/plandex · error

Error marshalling contexts:

Error message

Error marshalling contexts: 

What it means

After converting db contexts to API contexts, ListContextHandler json.Marshal's the []*shared.Context slice. Marshal only fails here if a Context field holds an unsupported type (e.g. channel, func, or cyclic data). In practice this is nearly impossible with fixed DB-backed structs, so the error signals an unexpected data/model problem, returned as HTTP 500.

Source

Thrown at app/server/handlers/plans_context.go:73

	})

	if err != nil {
		log.Printf("Error getting contexts: %v\n", err)
		http.Error(w, "Error getting contexts: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var apiContexts []*shared.Context

	for _, dbContext := range dbContexts {
		apiContexts = append(apiContexts, dbContext.ToApi())
	}

	bytes, err := json.Marshal(apiContexts)

	if err != nil {
		log.Printf("Error marshalling contexts: %v\n", err)
		http.Error(w, "Error marshalling contexts: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(bytes)
}

func GetContextBodyHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("Received request for GetContextBodyHandler")

	auth := Authenticate(w, r, true)
	if auth == nil {
		return
	}

	vars := mux.Vars(r)
	planId := vars["planId"]
	branch := vars["branch"]
	contextId := vars["contextId"]

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read err.Error() — Go names the exact unsupported type and struct field
  2. Check recent changes to shared.Context / db.Context.ToApi() for unserializable fields (func, chan, cycles)
  3. Add json:"-" tags to fields that must not be serialized, or give them custom MarshalJSON
  4. Rebuild/redeploy from a known-good release to confirm it's a code-level issue

Example fix

// before
type Context struct {
	loader func() error // unsupported by json.Marshal
	...
}
// after
type Context struct {
	loader func() error `json:"-"`
	...
}
Defensive patterns

Strategy: type-guard

Validate before calling

func marshalable(v any) bool {
	b, err := json.Marshal(v)
	return err == nil && b != nil
}

Type guard

func hasUnsupportedFields(v reflect.Value, seen map[uintptr]bool) bool {
	switch v.Kind() {
	case reflect.Chan, reflect.Func:
		return true
	case reflect.Ptr, reflect.Map:
		if v.IsNil() { return false }
		if seen[v.Pointer()] { return true }
		seen[v.Pointer()] = true
		defer delete(seen, v.Pointer())
	}
	switch v.Kind() {
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			if hasUnsupportedFields(v.Field(i), seen) { return true }
		}
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			if hasUnsupportedFields(v.Index(i), seen) { return true }
		}
	}
	return false
}

Try / catch

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

Prevention

When it happens

Trigger: json.Marshal(apiContexts) returns an error — an unsupported value in shared.Context (channel/func/cycle) introduced by a code or model change.

Common situations: A recent code change added an unserializable field to shared.Context or db.Context.ToApi(); custom build with modified model types.

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/893d7cfae35d350a. Report an issue: GitHub.