chenhg5/cc-connect · error

gemini: read chats dir: %w

Error message

gemini: read chats dir: %w

What it means

Reading ~/.gemini/tmp/<slug>/chats failed with an error other than NotExist (which is treated as an empty list). The wrapped error is returned from listGeminiSessions → ListSessions. This means the directory exists but is unreadable, or another I/O error occurred.

Source

Thrown at agent/gemini/gemini.go:520

	}
	return ""
}

func listGeminiSessions(workDir string) ([]core.AgentSessionInfo, error) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return nil, fmt.Errorf("gemini: cannot determine home dir: %w", err)
	}

	slug := geminiProjectSlug(workDir)
	chatsDir := filepath.Join(homeDir, ".gemini", "tmp", slug, "chats")

	entries, err := os.ReadDir(chatsDir)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("gemini: read chats dir: %w", err)
	}

	var sessions []core.AgentSessionInfo
	for _, entry := range entries {
		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
			continue
		}

		data, err := os.ReadFile(filepath.Join(chatsDir, entry.Name()))
		if err != nil {
			continue
		}

		var sf sessionFile
		if json.Unmarshal(data, &sf) != nil || sf.SessionID == "" {
			continue
		}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check `ls -la ~/.gemini/tmp/<slug>/chats` — fix permissions (chmod/chown) or remove a stray file that shadows the directory
  2. If the home dir is read-only, mount it writable or relocate HOME
  3. Inspect the wrapped inner error (`gemini: read chats dir: ...`) for the exact errno
  4. Restore ~/.gemini from a known-good state or let the gemini CLI recreate it

Example fix

// before
$ ls ~/.gemini/tmp/myproj/chats
ls: cannot open 'chats': Permission denied
// after
$ chmod 755 ~/.gemini ~/.gemini/tmp ~/.gemini/tmp/myproj/chats
$ sudo chown -R $USER ~/.gemini
Defensive patterns

Strategy: try-catch

Validate before calling

chats := filepath.Join(os.Getenv("HOME"), ".gemini", "tmp", slug, "chats")
if fi, err := os.Stat(chats); err == nil && !fi.IsDir() {
  return fmt.Errorf("%s exists but is not a directory", chats)
}

Try / catch

sessions, err := agent.ListSessions(ctx)
if err != nil && strings.Contains(err.Error(), "read chats dir") {
  var pe *fs.PathError
  if errors.As(err, &pe) { log.Printf("fs error on %s: %v", pe.Path, pe.Err) }
}

Prevention

When it happens

Trigger: os.ReadDir returns e.g. EACCES (permissions changed on ~/.gemini or ~/.gemini/tmp), ENOTDIR (a file named `chats` or an intermediate component replaced the directory), or the path is a symlink to an unreadable target — anything not os.IsNotExist.

Common situations: Backup/sync tool replaced ~/.gemini with a partial copy; permissions tightened by security tooling; ~/.gemini/tmp/<slug>/chats created as a regular file by a buggy script; read-only mounted home.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/69b1e4723040de8d. Report an issue: GitHub.