chenhg5/cc-connect · error
write: %w
Error message
write: %w
What it means
saveAttachment writes the attachment with os.WriteFile(path, data, 0o644) and wraps failures as "write: %w". After the directory exists and the name is sanitized with filepath.Base, this error means the actual file write failed, so the attachment never reaches disk and SendFile/SendImage return without notifying the chat.
Source
Thrown at platform/wps-agentspace/wpsagentspace.go:282
// 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 {
p.cancel()
}
p.mu.Lock()
if p.conn != nil {
_ = p.conn.Close()
}
p.mu.Unlock()
})
return nilView on GitHub (pinned to 4000b2338a)
Solutions
- Unwrap with errors.Is to classify: os.ErrPermission, os.ErrExist (path is a directory), or ENOSPC.
- Check free space (df -h ~/.cc-connect) and enlarge the volume or prune old attachments.
- Remove/replace a directory occupying the sanitized basename, or adjust disk quotas.
- Ensure the daemon user has write permission on ~/.cc-connect/attachments (chown/chmod).
Example fix
// before: no space check before writing large attachments
path, err := p.saveAttachment(name, img.Data)
// after (caller-side validation)
if len(img.Data) > 100<<20 {
return fmt.Errorf("attachment %s too large (%d bytes)", name, len(img.Data))
}
path, err := p.saveAttachment(name, img.Data) Defensive patterns
Strategy: validation
Validate before calling
// pre-flight writability check
probe := filepath.Join(dir, ".write-test")
if err := os.WriteFile(probe, nil, 0o644); err != nil {
return fmt.Errorf("attachments dir %s not writable: %w", dir, err)
}
os.Remove(probe)
if len(data) > 50<<20 {
return fmt.Errorf("attachment %s exceeds 50MB limit", name)
} Try / catch
if _, err := p.SendImage(rc, img); err != nil && strings.HasPrefix(err.Error(), "write:") {
var pe *fs.PathError
if errors.As(err, &pe) {
slog.Error("attachment write failed", "path", pe.Path, "cause", pe.Err)
}
} Prevention
- Monitor free space/inodes on the attachments partition and alert before ENOSPC.
- Enforce an attachment size limit before calling SendFile/SendImage.
- Prune old files from ~/.cc-connect/attachments on a schedule.
- Keep the daemon user consistent so files it creates remain writable to it.
When it happens
Trigger: SendFile/SendImage when: the target path exists as a directory (sanitized basename collides with a directory name); disk full (ENOSPC); quota exceeded; permission denied on the directory; the file was created between MkdirAll and WriteFile with restrictive permissions.
Common situations: Small root filesystems filling up with large sent files; attachment directories on network mounts that go read-only; two daemons with different UIDs sharing the same $HOME; images with names identical to pre-existing directories.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- read existing Agy hooks %s: %w
- write Agy hooks overlay: %w
- codex: write config.toml: %w
- kimi: read sessions dir: %w
- pi: read settings: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/35e6fd759856a3ad.
Report an issue: GitHub.