chenhg5/cc-connect · error

mkdir: %w

Error message

mkdir: %w

What it means

saveAttachment creates ~/.cc-connect/attachments with os.MkdirAll(dir, 0o755) and wraps failures as "mkdir: %w". This fires when the directory tree cannot be created or already exists as a non-directory, typically due to permissions or filesystem state, and aborts SendFile/SendImage.

Source

Thrown at platform/wps-agentspace/wpsagentspace.go:273

	if err != nil {
		return fmt.Errorf("wps-agentspace: SendImage: %w", err)
	}

	notice := fmt.Sprintf("🖼 图片已保存到本地:\n%s", path)
	return p.sendText(rc.ChatID, notice, rc)
}

// saveAttachment writes data to ~/.cc-connect/attachments/<name> and returns
// the absolute path. The filename is sanitized to a basename to prevent path
// traversal.
func (p *Platform) saveAttachment(name string, data []byte) (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("resolve home dir: %w", err)
	}
	dir := filepath.Join(home, ".cc-connect", "attachments")
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return "", fmt.Errorf("mkdir: %w", err)
	}

	name = filepath.Base(name)
	if name == "" || name == "." || name == "/" {
		name = fmt.Sprintf("file_%d", time.Now().UnixMilli())
	}
	path := filepath.Join(dir, name)
	if err := os.WriteFile(path, data, 0o644); err != nil {
		return "", fmt.Errorf("write: %w", err)
	}
	return path, nil
}

// Stop gracefully shuts down the platform.
func (p *Platform) Stop() error {
	p.stopOnce.Do(func() {
		p.stopped.Store(true)
		if p.cancel != nil {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped errno with errors.Is(err, os.ErrPermission) / syscall.ENOTDIR / ENOSPC to identify the exact cause.
  2. Fix ownership/permissions: chown -R the daemon user ~/.cc-connect, or chmod u+rwx on each path component.
  3. If ~/.cc-connect exists as a file, remove or rename it so the directory can be created.
  4. Mount a writable volume at ~/.cc-connect (or set HOME to a writable path) in containers.

Example fix

// before: diagnose only from generic failure
if err := os.MkdirAll(dir, 0o755); err != nil {
    return "", fmt.Errorf("mkdir: %w", err)
}
// after (caller-side guard)
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s exists and is not a directory", dir)
}
if err := os.MkdirAll(dir, 0o755); err != nil {
    return fmt.Errorf("mkdir: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

dir := filepath.Join(home, ".cc-connect", "attachments")
if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
    return fmt.Errorf("%s exists but is not a directory", dir)
}
if err := os.MkdirAll(dir, 0o755); err != nil {
    return fmt.Errorf("cannot create %s: %w", dir, err)
}

Try / catch

if _, err := p.SendFile(rc, file); err != nil && strings.HasPrefix(err.Error(), "mkdir:") {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, os.ErrPermission) {
        slog.Error("permission denied creating attachments dir", "path", pe.Path)
    }
}

Prevention

When it happens

Trigger: SendFile or SendImage when: $HOME is read-only or owned by another user; a component of the path (~/.cc-connect or ~/.cc-connect/attachments) exists as a regular file; the filesystem is read-only or full; SELinux/AppArmor denies the write.

Common situations: Container images with read-only rootfs; daemons running as non-root users while ~/.cc-connect was created by root during manual testing; macOS sandboxed launchd contexts; immutable infrastructure mounts.

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 chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/dc8499d37edcb5e4. Report an issue: GitHub.