sipeed/picoclaw · error
read input: %w
Error message
read input: %w
What it means
While the interactive model picker waited for input, bufio.Scanner over stdin returned an I/O error — not a clean EOF, which produces the separate 'no selection provided' message. The raw scanner error is wrapped with %w, so the text identifies the actual read failure. Only reachable when -m/--model is omitted, since a model id skips the picker entirely.
Source
Thrown at cmd/picoclaw/internal/model/add.go:135
return upsertModelDefault(opt.apiBase, opt.apiKey, opt.alias, selected, opt.stdout)
}
func pickModel(stdin io.Reader, stdout io.Writer, entries []modelEntry) (string, error) {
fmt.Fprintf(stdout, "\n%d model(s) available:\n", len(entries))
for i, m := range entries {
line := m.ID
if m.Name != "" && m.Name != m.ID {
line = fmt.Sprintf("%s (%s)", m.ID, m.Name)
}
fmt.Fprintf(stdout, " %3d) %s\n", i+1, line)
}
scanner := bufio.NewScanner(stdin)
for {
fmt.Fprint(stdout, "Pick a model (number or id): ")
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("read input: %w", err)
}
return "", fmt.Errorf("no selection provided")
}
text := strings.TrimSpace(scanner.Text())
if text == "" {
continue
}
if idx, err := strconv.Atoi(text); err == nil {
if idx < 1 || idx > len(entries) {
fmt.Fprintf(stdout, "Out of range. Enter 1-%d.\n", len(entries))
continue
}
return entries[idx-1].ID, nil
}
for _, m := range entries {
if m.ID == text {
return m.ID, nil
}View on GitHub (pinned to 49183d7e8d)
Solutions
- Pass the model id directly: picoclaw model add -m <model-id> — no stdin needed
- Interactively, run the command in a real TTY
- When scripting, feed a complete answer: printf '1\n' | picoclaw model add ...
- Read the wrapped error text — it names the underlying read failure (pipe closed, I/O timeout, etc.)
Example fix
# before (CI job; stdin is a broken pipe) $ picoclaw model add -b https://api.example.com/v1 -k sk-... read input: file already closed # after $ picoclaw model add -b https://api.example.com/v1 -k sk-... -m gpt-4o-mini
Defensive patterns
Strategy: validation
Validate before calling
func stdinIsInteractive() bool {
fi, err := os.Stdin.Stat()
return err == nil && fi.Mode()&os.ModeCharDevice != 0
}
if !stdinIsInteractive() && modelID == "" {
return fmt.Errorf("no TTY on stdin; pass -m <model-id> to skip the interactive picker")
} Type guard
func isReadInputError(err error) bool {
return err != nil && strings.Contains(err.Error(), "read input:")
} Try / catch
if err := runAdd(opt); err != nil {
if isReadInputError(err) {
// stdin is broken: retry with -m instead of prompting again
}
return err
} Prevention
- Never rely on interactive prompts in CI — always pass -m
- Check that variables feeding the pipeline are set before piping into picoclaw
- Run the command in a real terminal for interactive use
When it happens
Trigger: The input pipe is closed or broken mid-read: an upstream process in a pipeline exiting early (head -0 | picoclaw model add), an SSH session dropping while the prompt is open, or stdin attached to a special file that errors on read.
Common situations: Running picoclaw model add in CI or scripts where stdin is a closed or absent pipe; piping input from a command that itself fails; terminal sessions killed during the prompt.
Related errors
- no selection provided
- failed to confirm overwrite: %w
- unsupported --type %q (only 'openai-compatible' is supported
- fetch models: %w
- no models returned by %s
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/9395b2eebc800f25.
Report an issue: GitHub.