chenhg5/cc-connect · error

claudecode: open session file: %w

Error message

claudecode: open session file: %w

What it means

GetSessionHistory (agent/claudecode/claudecode.go:737) opens <projectDir>/<sessionID>.jsonl to parse transcript entries. If os.Open fails (file missing, permission denied, path is a directory), it wraps the error as 'claudecode: open session file: %w'. The underlying OS error is preserved, so inspect %w to distinguish not-exist from permission problems.

Source

Thrown at agent/claudecode/claudecode.go:737

// GetSessionHistory reads the Claude Code JSONL transcript and returns user/assistant messages.
func (a *Agent) GetSessionHistory(_ context.Context, sessionID string, limit int) ([]core.HistoryEntry, error) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return nil, err
	}
	a.mu.RLock()
	workDir := a.workDir
	a.mu.RUnlock()
	absWorkDir, _ := filepath.Abs(workDir)
	projectDir := findProjectDir(homeDir, absWorkDir)
	if projectDir == "" {
		return nil, fmt.Errorf("claudecode: project dir not found")
	}

	path := filepath.Join(projectDir, sessionID+".jsonl")
	f, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("claudecode: open session file: %w", err)
	}
	defer f.Close()

	var entries []core.HistoryEntry
	scanner := bufio.NewScanner(f)
	scanner.Buffer(make([]byte, 256*1024), 256*1024)

	for scanner.Scan() {
		var raw struct {
			Type      string `json:"type"`
			Timestamp string `json:"timestamp"`
			Message   struct {
				Role    string          `json:"role"`
				Content json.RawMessage `json:"content"`
			} `json:"message"`
		}
		if json.Unmarshal(scanner.Bytes(), &raw) != nil {
			continue

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use errors.Is(err, fs.ErrNotExist) to detect an unknown/stale session ID and return a friendly 'session not found' instead of the raw wrap.
  2. Verify the session ID with ListSessions(ctx, workDir) before querying history.
  3. Check file permissions: the user running cc-connect must be able to read ~/.claude/projects/<dir>/<id>.jsonl.
  4. If history was just requested for an active session, retry briefly — the transcript may not have been flushed yet.

Example fix

// before
entries, err := agent.GetSessionHistory(ctx, sessionID)
if err != nil { return err }
// after
entries, err := agent.GetSessionHistory(ctx, sessionID)
if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        return fmt.Errorf("no history for session %s", sessionID)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

p := filepath.Join(home, ".claude", "projects", encodedDir, sessionID+".jsonl")
if _, err := os.Stat(p); err != nil {
    return fmt.Errorf("no transcript at %s", p)
}

Try / catch

entries, err := agent.GetSessionHistory(ctx, id)
if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        return nil, fmt.Errorf("no history for session %s", id)
    }
    if errors.Is(err, fs.ErrPermission) {
        return nil, fmt.Errorf("cannot read transcript: check file permissions")
    }
    return err
}

Prevention

When it happens

Trigger: Calling GetSessionHistory(ctx, sessionID) where the project dir exists but the session file cannot be opened: unknown sessionID (no .jsonl file), file deleted mid-session, or read permission denied on the transcript.

Common situations: Requesting history for a session ID that belongs to a different work_dir/project; Claude Code cleaned old transcripts; running cc-connect as a service user lacking read access to another user's ~/.claude; typo'd or truncated session ID pasted from chat.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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