sipeed/picoclaw · error

write temp file: %w

Error message

write temp file: %w

What it means

Writing the downloaded inbound media bytes to the created temp file failed (media.go:336-339). Writes of up to 20 MiB go to os.TempDir()/picoclaw_media, so ENOSPC (disk/full quota) dominates; EIO and EDQUOT also surface here. The code already closes and unlinks the partial file, so no residue is left.

Source

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

		resp.Header.Get("Content-Disposition"),
	)
	ext := filepath.Ext(filename)
	if ext == "" {
		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
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. df -h <TMPDIR> - if tmpfs too small, give the media path a real volume or raise the container's memory limit (tmpfs counts against RAM)
  2. Set TMPDIR to the largest writable volume: ENV TMPDIR=/var/lib/picoclaw/tmp with a mounted PVC/volume
  3. If quota (EDQUOT): raise the user quota or run the service under a user with headroom
  4. Guard at the source: reject media near the limit when free space is insufficient

Example fix

# before: media spools to 64MiB tmpfs
resources:
  limits: { memory: 128Mi }

# after: spool to a dedicated writable volume
env: [{ name: TMPDIR, value: /var/lib/picoclaw/tmp }]
volumeMounts: [{ name: media, mountPath: /var/lib/picoclaw/tmp }]
Defensive patterns

Strategy: validation

Validate before calling

import "golang.org/x/sys/unix"

func tmpHasSpace(needBytes uint64) bool {
	var st unix.Statfs_t
	if err := unix.Statfs(os.TempDir(), &st); err != nil {
		return false
	}
	return uint64(st.Bavail)*uint64(st.Bsize) > needBytes+64<<20
}

Prevention

When it happens

Trigger: tmpfs /tmp smaller than the media (containers default tmpfs to a fraction of RAM); disk quota hit mid-write; NFS/overlayfs write errors; physical disk full.

Common situations: Kubernetes emptyDir/tmpfs sized 64 MiB receiving a 20 MiB video plus other pods' usage; CI runners with tiny scratch disks; shared hosts with per-user quotas.

Related errors


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