plandex-ai/plandex · error
error writing convo message: %v
Error message
error writing convo message: %v
What it means
StoreConvoMessage writes the marshalled JSON to <convoDir>/<message.Id>.json with os.WriteFile after successfully creating the directory. This error wraps that write failure; the directory exists but the file could not be created or fully written, so the convo message was not persisted (though the in-memory Id was already assigned).
Source
Thrown at app/server/db/convo_helpers.go:128
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)
}
var desc string
if message.Role == openai.ChatMessageRoleUser {
desc = "💬 User prompt"
// TODO: add user name
} else {
desc = "🤖 Plandex reply"
if message.Stopped {
desc += " | 🛑 " + color.New(color.FgHiRed).Sprint("stopped")
}
}View on GitHub (pinned to e2d772072e)
Solutions
- Check the wrapped *PathError: fix permissions (chown/chmod) on the convo directory if EACCES
- Free disk space / check quota if the error is ENOSPC or EDQUOT
- Ensure message.Id is either empty (it gets a fresh UUID) or a valid UUID before calling StoreConvoMessage
- Check volume/disk health (dmesg, smartctl) if the error indicates an I/O error
- Retry the store after fixing storage — the message was not committed yet, so a clean retry is safe
Example fix
// before
message.Id = userSuppliedId
err = os.WriteFile(filepath.Join(convoDir, message.Id+".json"), bytes, os.ModePerm)
// after
if message.Id == "" {
message.Id = uuid.New().String()
}
err = os.WriteFile(filepath.Join(convoDir, message.Id+".json"), bytes, os.ModePerm) Defensive patterns
Strategy: retry
Validate before calling
convoDir := getPlanConversationDir(orgId, planId)
if info, err := os.Stat(convoDir); err != nil || !info.IsDir() {
return fmt.Errorf("convo dir not ready: %s", convoDir)
}
if err := syscall.Access(convoDir, os.O_WRONLY); err != nil {
return fmt.Errorf("convo dir not writable: %w", err)
}
if free, err := diskFree(convoDir); err == nil && free < 10<<20 {
return fmt.Errorf("low disk: %d bytes free", free)
} Type guard
func isWritePathError(err error) bool {
var pe *fs.PathError
return errors.As(err, &pe) && pe.Op == "open"
} Try / catch
id, err := db.StoreConvoMessage(repo, msg, userId, branch, true)
if err != nil {
if strings.Contains(err.Error(), "writing convo message") && isTransient(err) {
time.Sleep(500 * time.Millisecond)
id, err = db.StoreConvoMessage(repo, msg, userId, branch, true) // safe: nothing committed yet
}
return err
} Prevention
- Monitor free disk space and inode usage on the data volume
- Enforce disk quotas headroom for message storage
- Never inject arbitrary message.Id values — let the library assign a UUID
- Check storage device health logs after any EIO error
- Ensure file permissions on convo dirs survive restores
When it happens
Trigger: os.WriteFile fails: permission denied on the existing convo directory, disk full (ENOSPC) mid-write, message.Id contains path-hostile characters (it shouldn't — it's a UUID — but an injected Id could break the path), I/O error on the storage device, or quota exceeded.
Common situations: Disk/quota exhaustion on busy servers storing many large replies; permission drift after a restore or container image change; NFS/EBS transient I/O errors; a message.Id supplied by a fork/plugin that is empty or contains a '/' creating an invalid path.
Related errors
- failed to write %s: %s
- error writing file: %v
- error writing hash file: %v
- error writing JSON file: %v
- error writing current plan settings: %v
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/f96e7f1143e1fd33.
Report an issue: GitHub.