kovidgoyal/kitty · error

Could not find any writable portals directories. Make sure X

Error message

Could not find any writable portals directories. Make sure XDG_DATA_HOME is set and point to a directory for which you have write permission.

What it means

enable_portal iterates writable data dirs trying to find-or-create <dir>/xdg-desktop-portal/portals. If none can be accessed or created (despite earlier writability checks), it fails before writing kitty.portal.

Source

Thrown at kittens/desktop_ui/portal.go:405

	return utils.UnsafeStringToBytes(strings.Join(lines, "\n"))
}

func enable_portal() (err error) {
	if len(WritableDataDirs()) == 0 {
		return fmt.Errorf("Could not find any writable data directories. Make sure XDG_DATA_DIRS is set and contains at least one directory for which you have write permission")
	}
	portals_dir := ""
	for _, x := range WritableDataDirs() {
		// Find-or-create the first available xdg-desktop-portals/portals directory
		q := filepath.Join(x, "xdg-desktop-portal", "portals")
		if (unix.Access(q, unix.W_OK) == nil && IsDir(q)) || (os.MkdirAll(q, 0o755) == nil) {
			portals_dir = q
			break
		}
	}
	if portals_dir == "" {
		return fmt.Errorf("Could not find any writable portals directories. Make sure XDG_DATA_HOME is set and point to a directory for which you have write permission.")
	}
	portals_defn := filepath.Join(portals_dir, "kitty.portal")
	if err = os.WriteFile(portals_defn, utils.UnsafeStringToBytes(fmt.Sprintf(
		`[portal]
DBusName=%s
Interfaces=%s;
`, PORTAL_BUS_NAME, strings.Join(AllPortalInterfaces(), ";"))), 0o644); err != nil {
		return err
	}
	fmt.Println("Wrote kitty portal definition to:", portals_defn)
	dbus_service_dir := ""
	for _, x := range WritableDataDirs() {
		q := filepath.Join(x, "dbus-1", "services")
		if err := os.MkdirAll(q, 0o755); err == nil {
			dbus_service_dir = q
			break
		}
	}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Check df -h and mount options for the data dir filesystem
  2. mkdir -p $XDG_DATA_HOME/xdg-desktop-portal/portals manually and fix any permission errors reported
  3. Remove any regular file blocking the path and retry enable
  4. Point XDG_DATA_HOME at a known-writable location
Defensive patterns

Strategy: validation

Validate before calling

q := filepath.Join(dataDir, "xdg-desktop-portal", "portals")
if err := os.MkdirAll(q, 0o755); err != nil || unix.Access(q, unix.W_OK) != nil {
    return fmt.Errorf("cannot use portals dir %s", q)
}

Prevention

When it happens

Trigger: WritableDataDirs returned entries, but every xdg-desktop-portal/portals subdirectory fails unix.Access(W_OK) and os.MkdirAll (e.g. disk full, read-only mount, permission changed mid-run, path component is a file).

Common situations: Disk full or filesystem mounted read-only after boot; a regular file blocking a directory path component; sandboxed environments blocking mkdir.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/058f0c6af9c770f6. Report an issue: GitHub.