plandex-ai/plandex · error

error creating convo dir: %v

Error message

error creating convo dir: %v

What it means

After marshalling, StoreConvoMessage ensures the plan's convo directory exists with os.MkdirAll (perm os.ModePerm). This error wraps the MkdirAll failure, meaning the directory (and any missing parents) could not be created and the message was not persisted.

Source

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

	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)
	}

	var desc string
	if message.Role == openai.ChatMessageRoleUser {
		desc = "💬 User prompt"
		// TODO: add user name

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped *PathError: if EACCES/EPERM fix ownership/permissions on the parent data directory (chown/chmod)
  2. If ENOTDIR/EEXIST, remove the regular file occupying the convoDir path and let MkdirAll recreate it
  3. Verify the data volume is mounted read-write (mount | grep, or container volume config)
  4. Free disk space if the error indicates ENOSPC
  5. Confirm the configured data-dir root exists and is writable by the server process

Example fix

// before
mount -o ro /dev/sdb /plandex-data
// after
mount -o remount,rw /plandex-data
chown -R plandex:plandex /plandex-data/plans
Defensive patterns

Strategy: validation

Validate before calling

parent := filepath.Dir(getPlanConversationDir(orgId, planId))
if info, err := os.Stat(parent); err != nil || !info.IsDir() {
    return fmt.Errorf("plans data dir missing or not a directory: %s", parent)
}
if err := syscall.Access(parent, os.O_WRONLY); err != nil {
    return fmt.Errorf("data dir not writable: %w", err)
}

Type guard

func isMkdirPathError(err error) (*fs.PathError, bool) {
    var pe *fs.PathError
    return pe, errors.As(err, &pe) && pe.Op == "mkdir"
}

Try / catch

id, err := db.StoreConvoMessage(repo, msg, userId, branch, false)
if err != nil {
    if strings.Contains(err.Error(), "creating convo dir") {
        // check mount is rw and ownership, then retry once
        if isVolumeReadOnly() { remountRW() }
        id, err = db.StoreConvoMessage(repo, msg, userId, branch, false)
    }
    return err
}

Prevention

When it happens

Trigger: os.MkdirAll(convoDir, os.ModePerm) fails: parent data-dir path missing on a read-only filesystem, permission denied on the parent directory, a regular file already exists at the convoDir path (MkdirAll returns ENOTDIR), path too long, or the volume is full/unavailable.

Common situations: Data volume mounted read-only after a container/storage change; data dir owned by another user (root after image rebuild); an operator accidentally created a file where the plan convo directory should be; disk full.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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