plandex-ai/plandex · error

error marshalling convo message: %v

Error message

error marshalling convo message: %v

What it means

StoreConvoMessage serializes the ConvoMessage to JSON with json.Marshal before persisting it; failure here returns this error and nothing is written. json.Marshal on a struct like this essentially only fails if the message contains values that cannot be represented in JSON (channels, funcs, invalid UTF-8 in strings, or a custom MarshalJSON returning an error).

Source

Thrown at app/server/db/convo_helpers.go:116

	return &convoMessage, nil
}

func StoreConvoMessage(repo *GitRepo, message *ConvoMessage, currentUserId, branch string, commit bool) (string, error) {
	convoDir := getPlanConversationDir(message.OrgId, message.PlanId)

	ts := time.Now().UTC()

	if message.Id == "" {
		message.Id = uuid.New().String()
	}

	message.CreatedAt = ts

	bytes, err := json.Marshal(message)

	if err != nil {
		return "", fmt.Errorf("error marshalling convo message: %v", err)
	}

	err = os.MkdirAll(convoDir, os.ModePerm)

	if err != nil {
		return "", fmt.Errorf("error creating convo dir: %v", err)
	}

	err = os.WriteFile(filepath.Join(convoDir, message.Id+".json"), bytes, os.ModePerm)

	if err != nil {
		return "", fmt.Errorf("error writing convo message: %v", err)
	}

	err = AddPlanConvoMessage(message, branch)

	if err != nil {
		return "", fmt.Errorf("error adding convo tokens: %v", err)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Log the full %v error — json.Marshal errors name the exact field/type that failed
  2. Remove or fix the non-serializable field on ConvoMessage (chan, func, unsafe pointer)
  3. Sanitize string fields (e.g. utf8.ValidString / strings.ToValidUTF8) before assigning model output
  4. Ensure any custom MarshalJSON implementations are nil-safe and error-free
  5. Write a unit test marshalling a fully-populated ConvoMessage to catch regressions

Example fix

// before
message.Message = modelRawOutput
bytes, err := json.Marshal(message)
// after
message.Message = strings.ToValidUTF8(modelRawOutput, "\ufffd")
bytes, err := json.Marshal(message)
Defensive patterns

Strategy: type-guard

Validate before calling

func marshalableConvoMessage(m *db.ConvoMessage) error {
    var probe db.ConvoMessage = *m
    _, err := json.Marshal(&probe)
    return err
}
// call before StoreConvoMessage
if err := marshalableConvoMessage(msg); err != nil { /* fix field */ }

Type guard

func isUTF8Safe(s string) bool { return utf8.ValidString(s) }

Try / catch

id, err := db.StoreConvoMessage(repo, msg, userId, branch, true)
if err != nil {
    if strings.Contains(err.Error(), "marshalling convo message") {
        // non-serializable field — log msg fields, fix input, do not retry blindly
        log.Printf("unserializable convo message fields: %+v", msg)
        return errSanitizeAndResend
    }
    return err
}

Prevention

When it happens

Trigger: json.Marshal(message) returns an error because a field of ConvoMessage (or a nested struct implementing MarshalJSON) is unmarshalable — e.g. a field was changed to chan/func type during development, a custom marshaller on a nested type errored, or a string field contains invalid UTF-8 from upstream model output.

Common situations: Custom code or a fork added a non-serializable field to ConvoMessage; a model/provider response with invalid UTF-8 got copied into message.Message; a plugin/middleware type with a failing MarshalJSON was embedded.

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/6c0653c91e643d5f. Report an issue: GitHub.