vxcontrol/pentagi · error

failed to create temp file: %w

Error message

failed to create temp file: %w

What it means

SaveToTemp creates the temporary file with os.CreateTemp(dir, ".resource-upload-*"); this error wraps CreateTemp's failure. Even when the directory exists, file creation can fail due to permissions, exhausted inodes, or filesystem errors.

Source

Thrown at backend/pkg/resources/resources.go:280

// EscapeLike escapes special LIKE pattern characters (%, _) in s so the string
// can be safely embedded in a SQL LIKE clause.
func EscapeLike(s string) string {
	s = strings.ReplaceAll(s, `\`, `\\`)
	s = strings.ReplaceAll(s, `%`, `\%`)
	s = strings.ReplaceAll(s, `_`, `\_`)
	return s
}

// SaveToTemp writes r into a new temporary file in dir and returns the path to
// the temp file. The caller is responsible for removing the temp file on error.
func SaveToTemp(r io.Reader, dir string) (tmpPath string, hash string, size int64, err error) {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", "", 0, fmt.Errorf("failed to create temp directory: %w", err)
	}

	tmp, err := os.CreateTemp(dir, ".resource-upload-*")
	if err != nil {
		return "", "", 0, fmt.Errorf("failed to create temp file: %w", err)
	}
	tmpPath = tmp.Name()
	defer tmp.Close()

	h := md5.New()
	mw := io.MultiWriter(tmp, h)
	written, copyErr := io.Copy(mw, r)
	if copyErr != nil {
		os.Remove(tmpPath)
		return "", "", 0, fmt.Errorf("failed to write temp file: %w", copyErr)
	}
	if err := tmp.Chmod(0644); err != nil {
		os.Remove(tmpPath)
		return "", "", 0, fmt.Errorf("failed to set temp file permissions: %w", err)
	}

	return tmpPath, hex.EncodeToString(h.Sum(nil)), written, nil
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check write permission on dir for the running user and the mount flags (mount | grep <dir>)
  2. Check open file descriptor limits (ulimit -n) and raise them if uploads fail under load
  3. Verify the volume is mounted rw and no security module is denying creation (dmesg/audit logs)
  4. Ensure dir still exists at call time — don't delete it concurrently

Example fix

// before
ulimit -n 1024  # too low for concurrent uploads
// after
ulimit -n 65536  # or set LimitNOFILE=65536 in the systemd unit
Defensive patterns

Strategy: retry

Validate before calling

func canCreateTemp(dir string) error {
	f, err := os.CreateTemp(dir, ".probe-*")
	if err != nil {
		return err
	}
	name := f.Name()
	f.Close()
	return os.Remove(name)
}

Try / catch

var tmpPath, hash string
var size int64
err := retry.Do(func() error {
	tp, h, s, e := resources.SaveToTemp(r, dir)
	if e != nil {
		var perr *fs.PathError
		if errors.As(e, &perr) && errors.Is(perr.Err, syscall.ENOSPC) {
			return retry.Unrecoverable(e) // don't retry disk-full
		}
		return e
	}
	tmpPath, hash, size = tp, h, s
	return nil
}, retry.Attempts(3))

Prevention

When it happens

Trigger: dir exists but is not writable by the process user; the filesystem ran out of inodes; security policies (e.g. read-only tmpfs, AppArmor/SELinux) block creation; too many open file descriptors. Raised via UploadResources or direct calls.

Common situations: ulimit -n exhaustion under heavy upload load; Kubernetes securityContext with readOnlyRootFilesystem; SELinux denials on container volumes; tmp-cleaner scripts removing the directory between MkdirAll and CreateTemp.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/f08a7eef3fdfafe2. Report an issue: GitHub.