{"record":{"id":"cacc61bd426c8b1f","repo":"sipeed/picoclaw","slug":"mkdir-media-dir-w","errorCode":null,"errorMessage":"mkdir media dir: %w","messagePattern":"mkdir media dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/wecom/media.go","lineNumber":329,"sourceCode":"\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"decrypt media: %w\", err)\n\t\t}\n\t}\n\n\tfilename, contentType := detectWeComMediaMetadata(\n\t\tdata,\n\t\tmsgID+fallbackExt,\n\t\tresp.Header.Get(\"Content-Type\"),\n\t\tresourceURL,\n\t\tresp.Header.Get(\"Content-Disposition\"),\n\t)\n\text := filepath.Ext(filename)\n\tif ext == \"\" {\n\t\text = inferMediaExt(contentType, fallbackExt)\n\t}\n\tmediaDir := filepath.Join(os.TempDir(), \"picoclaw_media\")\n\tif mkdirErr := os.MkdirAll(mediaDir, 0o700); mkdirErr != nil {\n\t\treturn \"\", fmt.Errorf(\"mkdir media dir: %w\", mkdirErr)\n\t}\n\ttmpFile, err := os.CreateTemp(mediaDir, msgID+\"-*\"+ext)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create temp file: %w\", err)\n\t}\n\ttmpPath := tmpFile.Name()\n\tif _, writeErr := tmpFile.Write(data); writeErr != nil {\n\t\t_ = tmpFile.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"write temp file: %w\", writeErr)\n\t}\n\tif closeErr := tmpFile.Close(); closeErr != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"close temp file: %w\", closeErr)\n\t}\n\n\tref, err := store.Store(tmpPath, media.MediaMeta{\n\t\tFilename:      filename,","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/wecom/media.go#L311-L347","documentation":"os.MkdirAll(os.TempDir()/picoclaw_media, 0700) failed while spooling inbound WeCom media (media.go:327-329). The %w wraps an *fs.PathError whose errno names the real problem: EACCES/EROFS on read-only or foreign-owned temp dirs, or the path exists as a regular file.","triggerScenarios":"TMPDIR points to a missing or read-only directory (hardened containers, scratch images); a previous run or another tool left a regular file named picoclaw_media in the temp dir; the process user lacks write permission on TMPDIR.","commonSituations":"Docker/Kubernetes containers with read-only rootfs and unset writable TMPDIR; running under systemd with PrivateTmp=true while TMPDIR points at a host path; multi-user hosts where /tmp is 1777 but a stale non-directory blocks the name.","solutions":["Read the wrapped *fs.PathError: EACCES/EROFS means permissions/read-only, EEXIST-as-file means a stale file owns the name","Set TMPDIR to a writable path for the process: Environment=TMPDIR=/var/lib/picoclaw/tmp (systemd) or ENV TMPDIR=/tmp/picoclaw (container with a writable mount)","os.Stat the dir: if it exists as a file, remove it (rm $TMPDIR/picoclaw_media) once, then retry","Verify with: TMPDIR=<dir> touch <dir>/probe from the same user the service runs as"],"exampleFix":"# before: container runs read-only, TMPDIR unset\nRUN chmod 555 /tmp\n\n# after: dedicated writable temp mount\nVOLUME [\"/tmp\"]\nENV TMPDIR=/tmp","handlingStrategy":"validation","validationCode":"// verify the spool dir is usable before media arrives\nfunc mediaDirReady() error {\n    dir := filepath.Join(os.TempDir(), \"picoclaw_media\")\n    if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {\n        return fmt.Errorf(\"%s exists and is not a directory\", dir)\n    }\n    if err := os.MkdirAll(dir, 0o700); err != nil {\n        return err\n    }\n    probe, err := os.CreateTemp(dir, \"probe-*\") // confirms write access\n    if err != nil {\n        return err\n    }\n    probe.Close()\n    return os.Remove(probe.Name())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["health-check the spool dir at startup, not on the first inbound media message","pin TMPDIR explicitly in the service unit/container instead of inheriting the shell's","monitor disk/inode headroom on the temp volume"],"tags":["filesystem","temp-dir","permissions","deployment"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}