sipeed/picoclaw · error
session not found
Error message
session not found
What it means
Sentinel error from the process-session manager in pkg/tools. It is returned by GetSession (session.go:235) when the registry holds no session under the given ID, and by killProcess (session.go:108) when the session's PID is <= 0, so there is no live OS process to act on. Callers in pkg/tools/shell.go (lines 753, 782, 818, 857, 1075) branch on errors.Is(err, ErrSessionNotFound) to convert it into a user-facing tool error rather than a crash.
Source
Thrown at pkg/tools/session.go:30
)
const maxOutputBufferSize = 1 * 1024 * 1024 // 1MB
const outputTruncateMarker = "\n... [output truncated, exceeded 1MB]\n"
// PtyKeyMode represents arrow key encoding mode for PTY sessions.
// Programs send smkx/rmkx sequences to switch between CSI and SS3 modes.
type PtyKeyMode uint8
const (
PtyKeyModeCSI PtyKeyMode = iota // triggered by rmkx (\x1b[?1l)
PtyKeyModeSS3 // triggered by smkx (\x1b[?1h)
)
const PtyKeyModeNotFound PtyKeyMode = 255
var (
ErrSessionNotFound = errors.New("session not found")
ErrSessionDone = errors.New("session already completed")
ErrPTYNotSupported = errors.New("PTY is not supported on this platform")
ErrNoStdin = errors.New("no stdin available")
)
type ProcessSession struct {
mu sync.Mutex
ID string
PID int
Command string
PTY bool
Background bool
StartTime int64
ExitCode int
Status string
stdinWriter io.Writer
stdoutPipe io.Reader
outputBuffer *bytes.BufferView on GitHub (pinned to 49183d7e8d)
Solutions
- Re-list active sessions (the shell tool's session list) and use a currently running ID
- Check the session Status field before calling Kill/Write; skip work if it is not "running"
- If the ID came from a previous process lifetime, re-spawn the session instead of reusing the ID
- In library code, handle it with errors.Is(err, tools.ErrSessionNotFound) and degrade gracefully instead of returning a raw error
Example fix
// before
err := sess.Kill()
if err != nil {
return err // surfaces "session not found" to the user
}
// after
err := sess.Kill()
if errors.Is(err, tools.ErrSessionNotFound) {
log.Printf("session %s already gone; nothing to kill", id)
return nil
}
if err != nil {
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
if sess := manager.Get(id); sess == nil {
// re-list sessions and pick a live one instead of proceeding
ids := manager.ListIDs()
return fmt.Errorf("session %s gone; active: %v", id, ids)
} Type guard
func isSessionNotFound(err error) bool {
return errors.Is(err, tools.ErrSessionNotFound)
} Try / catch
if err := sess.Kill(); err != nil {
if errors.Is(err, tools.ErrSessionNotFound) {
// already gone — treat as success in cleanup paths
return nil
}
return fmt.Errorf("kill session %s: %w", id, err)
} Prevention
- Never cache session IDs across daemon restarts; the registry is in-memory
- Check sess.Status == "running" before Write or Kill
- Always use errors.Is against the sentinels, never string matching on the message
- Re-list sessions when an agent resumes after any pause
When it happens
Trigger: Passing a session ID that was never created, one from a previous run of the binary (registry is in-memory and resets on restart), or one whose session already exited and was removed. Also triggered by Kill() on a session whose PID field is 0 or negative.
Common situations: Agent tool invocations that cache a session ID across a daemon restart; racing a background process that finishes between listing sessions and writing to it; tests that construct ProcessSession structs directly without a PID.
Related errors
- session already completed
- failed to load MCP servers: %w
- create process hook stdin: %w
- create process hook stdout: %w
- create process hook stderr: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/501498b7e5965121.
Report an issue: GitHub.