{"record":{"id":"16e6ebe75e954da9","repo":"wavetermdev/waveterm","slug":"reading-from-stdin-w","errorCode":null,"errorMessage":"reading from stdin: %w","messagePattern":"reading from stdin: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/wsh/cmd/wshcmd-ai.go","lineNumber":103,"sourceCode":"\tif len(args) > maxFileCount {\n\t\treturn fmt.Errorf(\"too many files (maximum %d files allowed)\", maxFileCount)\n\t}\n\n\tfor _, filePath := range args {\n\t\tvar data []byte\n\t\tvar fileName string\n\t\tvar mimeType string\n\t\tvar err error\n\n\t\tif filePath == \"-\" {\n\t\t\tif stdinUsed {\n\t\t\t\treturn fmt.Errorf(\"stdin (-) can only be used once\")\n\t\t\t}\n\t\t\tstdinUsed = true\n\n\t\t\tdata, err = io.ReadAll(os.Stdin)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"reading from stdin: %w\", err)\n\t\t\t}\n\t\t\tfileName = \"stdin\"\n\t\t\tmimeType = \"text/plain\"\n\t\t} else {\n\t\t\tfileInfo, err := os.Stat(filePath)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"accessing file %s: %w\", filePath, err)\n\t\t\t}\n\t\t\tabsPath, err := filepath.Abs(filePath)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"getting absolute path for %s: %w\", filePath, err)\n\t\t\t}\n\n\t\t\tif fileInfo.IsDir() {\n\t\t\t\tresult, err := fileutil.ReadDir(filePath, 500)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"reading directory %s: %w\", filePath, err)\n\t\t\t\t}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/cmd/wsh/cmd/wshcmd-ai.go#L85-L121","documentation":"Wraps any error from io.ReadAll(os.Stdin) when the `-` argument is used in `wsh ai`. Reading stdin failed at the OS level (I/O error, closed/broken pipe, EINTR, etc.). The original error is preserved via %w for errors.Is/As inspection.","triggerScenarios":"`wsh ai -` where stdin is closed or not connected (no pipe/redirection in a non-tty context), the upstream writer process died mid-stream (broken pipe), or an OS I/O error (disk/device failure, interrupted read) occurs during the read.","commonSituations":"CI jobs invoking `wsh ai -` without piping anything and stdin closed; `someproc | wsh ai -` where someproc crashed; running under a supervisor that detached stdin.","solutions":["Ensure stdin is actually provided: `cat file | wsh ai -` instead of a bare `wsh ai -` in a non-interactive shell.","Check the upstream producer did not exit early; rerun the pipeline and fix the producing command first (inspect the wrapped error).","If stdin may be closed, pass a file path instead of `-`.","Retry on transient I/O errors (EINTR) — rerun the command."],"exampleFix":"// before (in CI, stdin closed)\n$ wsh ai -\n// after\n$ cat report.txt | wsh ai - --message \"summarize\"","handlingStrategy":"try-catch","validationCode":"if [ -t 0 ] || [ ! -r /dev/stdin ]; then\n  echo \"wsh ai '-' requires piped stdin, e.g. cat file | wsh ai -\" >&2\n  exit 1\nfi","typeGuard":null,"tryCatchPattern":"if err := runAI(); err != nil {\n    var perr *os.PathError\n    if errors.As(err, &perr) && errors.Is(err, syscall.EPIPE) {\n        // upstream writer died; retry the pipeline after fixing the producer\n    } else {\n        return fmt.Errorf(\"stdin read failed: %w\", err)\n    }\n}","preventionTips":["Always pair `-` with an explicit pipe or redirect; never run `wsh ai -` bare in CI.","Check the upstream command's exit status before consuming its output.","Prefer passing a file path over `-` in non-interactive contexts."],"tags":["stdin","io","cli","broken-pipe"],"backgroundTag":"stdin-read-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}