{"record":{"id":"bde7b747abbd4a20","repo":"sipeed/picoclaw","slug":"close-temp-file-w","errorCode":null,"errorMessage":"close temp file: %w","messagePattern":"close temp file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/wecom/media.go","lineNumber":343,"sourceCode":"\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,\n\t\tContentType:   contentType,\n\t\tSource:        \"wecom\",\n\t\tCleanupPolicy: media.CleanupPolicyDeleteOnCleanup,\n\t}, scope)\n\tif err != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", err\n\t}\n\treturn ref, nil\n}\n\nfunc detectLocalWeComContentType(localPath, hint string) string {\n\tcontentType := normalizeWeComContentType(hint)\n\tif !isGenericWeComContentType(contentType) {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/wecom/media.go#L325-L361","documentation":"Closing the spooled inbound media file failed (media.go:341-343). On local filesystems Close is where buffered data is flushed, so a full disk (ENOSPC) or I/O error (EIO) first appears here even though Write succeeded; NFS/overlayfs can also defer write-back errors to Close. The code unlinks the file on this failure, so nothing partial is stored.","triggerScenarios":"Filesystem accepted writes into cache but failed the flush at Close: ENOSPC after quota enforcement, EIO on failing disks, NFS stale-handle/async errors on a /tmp mounted over NFS.","commonSituations":"Overlayfs container layers with thin-provisioned storage that filled up; NFS home dirs used as TMPDIR; degraded RAID surfacing EIO.","solutions":["Treat it exactly like a write failure: check dmesg/df and the wrapped *fs.PathError errno","Rule out deferred-ENOSPC by checking free space at failure time; free space and retry the message","Do not put the media spool on NFS - use local SSD or a volume with reliable write-back","If EIO persists: inspect device health (smartctl) before re-accepting media traffic"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import \"io/fs\"\n\nfunc isFsErr(err error, target error) bool { return errors.Is(err, target) }\n// usage: isFsErr(err, fs.ErrNotExist), isFsErr(err, fs.ErrPermission)","tryCatchPattern":"// Close errors are real write failures - never _ = f.Close() on data files\nerr = tmpFile.Write(data)\nif cerr := tmpFile.Close(); err == nil && cerr != nil {\n    _ = os.Remove(tmpPath)\n    err = fmt.Errorf(\"close temp file: %w\", cerr)\n}","preventionTips":["check the errno in the wrapped fs.PathError before assuming corruption","keep the spool off NFS and overlay-on-thin-provisioning","test the deployment by spooling a full-size 20 MiB file at startup"],"tags":["filesystem","disk-full","io","nfs"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}