{"record":{"id":"990edaec117c3d91","repo":"chenhg5/cc-connect","slug":"wps-agentspace-sendimage-w","errorCode":null,"errorMessage":"wps-agentspace: SendImage: %w","messagePattern":"wps-agentspace: SendImage: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"platform/wps-agentspace/wpsagentspace.go","lineNumber":256,"sourceCode":"\t\treturn fmt.Errorf(\"wps-agentspace: SendImage: invalid reply context type %T\", replyCtx)\n\t}\n\n\tname := img.FileName\n\tif name == \"\" {\n\t\text := \".png\"\n\t\tif strings.HasPrefix(img.MimeType, \"image/jpeg\") {\n\t\t\text = \".jpg\"\n\t\t} else if strings.HasPrefix(img.MimeType, \"image/gif\") {\n\t\t\text = \".gif\"\n\t\t} else if strings.HasPrefix(img.MimeType, \"image/webp\") {\n\t\t\text = \".webp\"\n\t\t}\n\t\tname = \"image_\" + time.Now().Format(\"20060102_150405\") + ext\n\t}\n\n\tpath, err := p.saveAttachment(name, img.Data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"wps-agentspace: SendImage: %w\", err)\n\t}\n\n\tnotice := fmt.Sprintf(\"🖼 图片已保存到本地：\\n%s\", path)\n\treturn p.sendText(rc.ChatID, notice, rc)\n}\n\n// saveAttachment writes data to ~/.cc-connect/attachments/<name> and returns\n// 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}","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/wps-agentspace/wpsagentspace.go#L238-L274","documentation":"SendImage wraps any failure from saveAttachment (home-dir resolution, mkdir, or file write) into \"wps-agentspace: SendImage: %w\". Because the platform has no image-upload API surface here, it persists the image to ~/.cc-connect/attachments/ and sends a text notice with the local path; this error means that persistence step failed, so no message was sent to the chat.","triggerScenarios":"Calling SendImage when os.UserHomeDir() fails, ~/.cc-connect/attachments cannot be created (permissions, read-only $HOME, full disk), or os.WriteFile fails (disk quota, name collision with a directory). All underlying errors are joined via %w, so errors.Is/As unwrapping works.","commonSituations":"Running the cc-connect daemon as a system service whose $HOME is unset or points to a non-writable directory (e.g. systemd with no HOME=, or /nonexistent); read-only root filesystems in containers; disk full after large attachment uploads.","solutions":["Check the wrapped cause: errors.Is(err, os.ErrPermission), errors.Is(err, fs.ErrNotExist), or inspect via errors.Unwrap to see if it is \"resolve home dir\", \"mkdir\", or \"write\".","Ensure $HOME is set and writable by the daemon user; for systemd units add Environment=HOME=/var/lib/cc-connect and make that directory writable.","Manually verify mkdir -p ~/.cc-connect/attachments succeeds as the daemon user; fix permissions (chown/chmod) if not.","Free disk space or raise quota if the cause is a write failure (ENOSPC)."],"exampleFix":"// before (systemd unit, HOME unset for service user)\n[Service]\nUser=ccconnect\nExecStart=/usr/local/bin/cc-connect\n// after\n[Service]\nUser=ccconnect\nEnvironment=HOME=/var/lib/cc-connect\nExecStart=/usr/local/bin/cc-connect","handlingStrategy":"try-catch","validationCode":"home, err := os.UserHomeDir()\nif err != nil {\n    return fmt.Errorf(\"cannot send image: home dir unresolvable: %w\", err)\n}\ndir := filepath.Join(home, \".cc-connect\", \"attachments\")\nif fi, err := os.Stat(dir); err != nil || !fi.IsDir() {\n    return fmt.Errorf(\"attachments dir %s missing\", dir)\n}","typeGuard":null,"tryCatchPattern":"if err := p.SendImage(rc, img); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        slog.Error(\"image save failed on path\", \"op\", pe.Op, \"path\", pe.Path, \"err\", pe.Err)\n    } else {\n        slog.Error(\"image send failed\", \"err\", err)\n    }\n}","preventionTips":["Run the daemon with a guaranteed writable $HOME (set it explicitly in the service unit).","Pre-create ~/.cc-connect/attachments at install/first-run time with correct ownership.","Monitor disk space on the partition holding ~/.cc-connect.","Always unwrap with errors.Is/As to distinguish home-dir, mkdir, and write failures before alerting."],"tags":["go","file-write","attachments","error-wrapping"],"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-14T05:17:10.506Z"}