multica-ai/multica · error

chmod temp file: %w

Error message

chmod temp file: %w

What it means

The temp file is chmod-ed to the original binary's mode so the replaced executable stays executable; failure is wrapped as 'chmod temp file: %w' and the temp file removed. Because CreateTemp makes 0600 files, skipping this step would break the rename, so the update aborts instead.

Source

Thrown at server/internal/cli/update.go:468

	}
	tmpPath := tmpFile.Name()

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

	// Preserve original file permissions.
	info, err := os.Stat(exePath)
	if err != nil {
		os.Remove(tmpPath)
		return "", fmt.Errorf("stat original binary: %w", err)
	}
	if err := os.Chmod(tmpPath, info.Mode()); err != nil {
		os.Remove(tmpPath)
		return "", fmt.Errorf("chmod temp file: %w", err)
	}

	// Replace the original binary. On Windows this moves the running executable
	// aside first; on Unix a plain rename over the running inode is fine.
	if err := replaceBinary(tmpPath, exePath); err != nil {
		os.Remove(tmpPath)
		return "", fmt.Errorf("replace binary: %w", err)
	}

	return fmt.Sprintf("Downloaded %s and replaced %s", assetName, exePath), nil
}

// extractBinaryFromTarGz reads a .tar.gz stream and returns the contents of the
// named file entry.
func extractBinaryFromTarGz(r io.Reader, name string) ([]byte, error) {
	gz, err := gzip.NewReader(r)
	if err != nil {
		return nil, fmt.Errorf("gzip reader: %w", err)

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Move the binary to a POSIX-capable local filesystem (ext4, APFS, NTFS with Go-supported ACLs).
  2. Adjust SELinux/AppArmor policy or the directory ACLs to allow chmod by the running user.
  3. Ensure the process owns the files it creates in the install directory.
  4. Check the wrapped error path — it names the temp file that failed.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(exePath); err == nil {
    if info.Mode().Perm()&0111 == 0 {
        // original not executable; chmod before update to keep replace sane
    }
}

Try / catch

out, err := cli.UpdateViaDownload(ver)
if err != nil && strings.HasPrefix(err.Error(), "chmod temp file") {
    // filesystem or MAC policy denies chmod: relocate binary to a POSIX fs
}

Prevention

When it happens

Trigger: Filesystems that do not support chmod (some FAT/exFAT mounts, certain network filesystems, Windows ACL edge cases); the user losing ownership of the temp file mid-update; SELinux/AppArmor denying chmod on the path.

Common situations: Running the binary off an exFAT USB drive or a permissive NFS mount; hardened Linux with MAC policies on the install directory; Windows directories with restrictive inherited ACLs.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/a14da7017bf0b427. Report an issue: GitHub.