fatedier/frp · error

failed to create temp file: %w

Error message

failed to create temp file: %w

What it means

os.OpenFile on Path + ".tmp" (O_WRONLY|O_CREATE|O_TRUNC, mode 0600) failed, so the atomic-write sequence could not even start. Everything after this (write, fsync, rename) is skipped; the in-memory change that triggered the save has already been applied and will be rolled back by persistOrRollbackUnlocked.

Source

Thrown at pkg/config/source/store.go:152

	for _, v := range s.visitors {
		stored.Visitors = append(stored.Visitors, v1.TypedVisitorConfig{VisitorConfigurer: v})
	}

	data, err := jsonx.MarshalIndent(stored, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}

	dir := filepath.Dir(s.config.Path)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return fmt.Errorf("failed to create directory: %w", err)
	}

	tmpPath := s.config.Path + ".tmp"

	f, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
	if err != nil {
		return fmt.Errorf("failed to create temp file: %w", err)
	}

	if _, err := f.Write(data); err != nil {
		f.Close()
		os.Remove(tmpPath)
		return fmt.Errorf("failed to write temp file: %w", err)
	}

	if err := f.Sync(); err != nil {
		f.Close()
		os.Remove(tmpPath)
		return fmt.Errorf("failed to sync temp file: %w", err)
	}

	if err := f.Close(); err != nil {
		os.Remove(tmpPath)
		return fmt.Errorf("failed to close temp file: %w", err)
	}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Confirm write permission on the directory: touch <dir>/.wtest as the running user
  2. If a DIRECTORY named <Path>.tmp exists, remove or rename it
  3. For read-only mounts, remount rw or relocate Path to a writable volume
  4. Check audit logs (ausearch -m avc / dmesg) for SELinux/AppArmor denials if permissions look fine

Example fix

# before: .tmp path occupied by a directory
ls -la /var/lib/frpc/
# store.json.tmp/  <- directory, OpenFile fails

# after
rm -rf /var/lib/frpc/store.json.tmp
Defensive patterns

Strategy: validation

Validate before calling

func canCreateTempFile(path string) error {
	tmp := path + ".tmp"
	if fi, err := os.Stat(tmp); err == nil && fi.IsDir() {
		return fmt.Errorf("%s exists and is a directory", tmp)
	}
	f, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
	if err != nil {
		return fmt.Errorf("cannot write %s: %w", tmp, err)
	}
	f.Close()
	os.Remove(tmp)
	return nil
}

Prevention

When it happens

Trigger: Directory not writable for the process user; read-only filesystem mount; an existing directory named exactly <Path>.tmp blocks file creation; SELinux/AppArmor denial; path length over NAME_MAX because of the .tmp suffix.

Common situations: Container with a read-only or root-owned volume mounted at the store directory; a leftover <store>.tmp directory from an earlier interrupted run or backup tool; security policy blocking writes outside allowed labels.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/586155fa2f916684. Report an issue: GitHub.