sipeed/picoclaw · error

close temp file: %w

Error message

close temp file: %w

What it means

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.

Source

Thrown at pkg/channels/wecom/media.go:343

		ext = inferMediaExt(contentType, fallbackExt)
	}
	mediaDir := filepath.Join(os.TempDir(), "picoclaw_media")
	if mkdirErr := os.MkdirAll(mediaDir, 0o700); mkdirErr != nil {
		return "", fmt.Errorf("mkdir media dir: %w", mkdirErr)
	}
	tmpFile, err := os.CreateTemp(mediaDir, msgID+"-*"+ext)
	if err != nil {
		return "", fmt.Errorf("create temp file: %w", err)
	}
	tmpPath := tmpFile.Name()
	if _, writeErr := tmpFile.Write(data); writeErr != nil {
		_ = tmpFile.Close()
		_ = os.Remove(tmpPath)
		return "", fmt.Errorf("write temp file: %w", writeErr)
	}
	if closeErr := tmpFile.Close(); closeErr != nil {
		_ = os.Remove(tmpPath)
		return "", fmt.Errorf("close temp file: %w", closeErr)
	}

	ref, err := store.Store(tmpPath, media.MediaMeta{
		Filename:      filename,
		ContentType:   contentType,
		Source:        "wecom",
		CleanupPolicy: media.CleanupPolicyDeleteOnCleanup,
	}, scope)
	if err != nil {
		_ = os.Remove(tmpPath)
		return "", err
	}
	return ref, nil
}

func detectLocalWeComContentType(localPath, hint string) string {
	contentType := normalizeWeComContentType(hint)
	if !isGenericWeComContentType(contentType) {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Treat it exactly like a write failure: check dmesg/df and the wrapped *fs.PathError errno
  2. Rule out deferred-ENOSPC by checking free space at failure time; free space and retry the message
  3. Do not put the media spool on NFS - use local SSD or a volume with reliable write-back
  4. If EIO persists: inspect device health (smartctl) before re-accepting media traffic
Defensive patterns

Strategy: try-catch

Type guard

import "io/fs"

func isFsErr(err error, target error) bool { return errors.Is(err, target) }
// usage: isFsErr(err, fs.ErrNotExist), isFsErr(err, fs.ErrPermission)

Try / catch

// Close errors are real write failures - never _ = f.Close() on data files
err = tmpFile.Write(data)
if cerr := tmpFile.Close(); err == nil && cerr != nil {
    _ = os.Remove(tmpPath)
    err = fmt.Errorf("close temp file: %w", cerr)
}

Prevention

When it happens

Trigger: 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.

Common situations: Overlayfs container layers with thin-provisioned storage that filled up; NFS home dirs used as TMPDIR; degraded RAID surfacing EIO.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/bde7b747abbd4a20. Report an issue: GitHub.