{"record":{"id":"35e6fd759856a3ad","repo":"chenhg5/cc-connect","slug":"write-w-35e6fd","errorCode":null,"errorMessage":"write: %w","messagePattern":"write: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/wps-agentspace/wpsagentspace.go","lineNumber":282,"sourceCode":"// the absolute path. The filename is sanitized to a basename to prevent path\n// traversal.\nfunc (p *Platform) saveAttachment(name string, data []byte) (string, error) {\n\thome, err := os.UserHomeDir()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve home dir: %w\", err)\n\t}\n\tdir := filepath.Join(home, \".cc-connect\", \"attachments\")\n\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\treturn \"\", fmt.Errorf(\"mkdir: %w\", err)\n\t}\n\n\tname = filepath.Base(name)\n\tif name == \"\" || name == \".\" || name == \"/\" {\n\t\tname = fmt.Sprintf(\"file_%d\", time.Now().UnixMilli())\n\t}\n\tpath := filepath.Join(dir, name)\n\tif err := os.WriteFile(path, data, 0o644); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write: %w\", err)\n\t}\n\treturn path, nil\n}\n\n// Stop gracefully shuts down the platform.\nfunc (p *Platform) Stop() error {\n\tp.stopOnce.Do(func() {\n\t\tp.stopped.Store(true)\n\t\tif p.cancel != nil {\n\t\t\tp.cancel()\n\t\t}\n\t\tp.mu.Lock()\n\t\tif p.conn != nil {\n\t\t\t_ = p.conn.Close()\n\t\t}\n\t\tp.mu.Unlock()\n\t})\n\treturn nil","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wps-agentspace/wpsagentspace.go#L264-L300","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before: no space check before writing large attachments\npath, err := p.saveAttachment(name, img.Data)\n// after (caller-side validation)\nif len(img.Data) > 100<<20 {\n    return fmt.Errorf(\"attachment %s too large (%d bytes)\", name, len(img.Data))\n}\npath, err := p.saveAttachment(name, img.Data)","handlingStrategy":"validation","validationCode":"// pre-flight writability check\nprobe := filepath.Join(dir, \".write-test\")\nif err := os.WriteFile(probe, nil, 0o644); err != nil {\n    return fmt.Errorf(\"attachments dir %s not writable: %w\", dir, err)\n}\nos.Remove(probe)\nif len(data) > 50<<20 {\n    return fmt.Errorf(\"attachment %s exceeds 50MB limit\", name)\n}","typeGuard":null,"tryCatchPattern":"if _, err := p.SendImage(rc, img); err != nil && strings.HasPrefix(err.Error(), \"write:\") {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        slog.Error(\"attachment write failed\", \"path\", pe.Path, \"cause\", pe.Err)\n    }\n}","preventionTips":["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."],"tags":["go","file-write","disk-space","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}