grpc-ecosystem/grpc-gateway · error
read %s: %w
Error message
read %s: %w
What it means
run reads each input file with os.ReadFile and wraps any OS-level read failure as `read <path>: <underlying error>`. This is the standard go idiom for file I/O errors: the wrapped error preserves the exact cause (file not found, permission denied, is a directory, etc.) while naming the path that failed.
Source
Thrown at openapiv3-merge/main.go:46
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "openapiv3-merge:", err)
os.Exit(1)
}
}
// run is the testable entry point. It accepts the program's arguments (no
// program name) and the writer to emit the merged document to.
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errors.New("usage: openapiv3-merge FILE [FILE ...]")
}
inputs := make([]merge.Input, 0, len(args))
for _, path := range args {
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read %s: %w", path, err)
}
inputs = append(inputs, merge.Input{Name: path, Data: data})
}
merged, err := merge.Merge(inputs)
if err != nil {
return err
}
if _, err := out.Write(merged); err != nil {
return err
}
if _, err := io.WriteString(out, "\n"); err != nil {
return err
}
return nil
}
View on GitHub (pinned to a58a4436a3)
Solutions
- Check the path with `ls -l <path>`; fix typos and run from the correct working directory
- If the error is permission denied, adjust file permissions or run with appropriate access
- Confirm the file exists inside the container/CI environment (mount volumes, copy artifacts)
- Verify you pass file paths, not directory paths
- Use absolute paths or paths relative to the actual CWD
Example fix
// before openapiv3-merge ./specs/api.jsoin // after openapiv3-merge ./specs/api.json
Defensive patterns
Strategy: try-catch
Validate before calling
for _, path := range args {
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("input %s is not accessible: %w", path, err)
}
if info.IsDir() {
return fmt.Errorf("input %s is a directory, expected a file", path)
}
} Try / catch
if err := run(os.Args[1:]); err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrNotExist) {
fmt.Fprintf(os.Stderr, "file not found: %s\n", pathErr.Path)
os.Exit(1)
}
if errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrPermission) {
fmt.Fprintf(os.Stderr, "permission denied: %s\n", pathErr.Path)
os.Exit(1)
}
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} Prevention
- Run the CLI from the directory containing the spec files or use absolute paths
- Stat each input path before invoking the merge
- In containers/CI, verify spec files are mounted/copied into the workspace
- Distinguish file paths from directories in argument handling
- Tab-complete paths to avoid typos
When it happens
Trigger: Invoking `openapiv3-merge FILE...` with a path that does not exist (ENOENT), is unreadable due to permissions (EACCES), is a directory, or otherwise fails os.ReadFile. Also triggered by tests calling run with a missing file path.
Common situations: Typo in the filename or wrong working directory; running inside a container where the spec file was not mounted; the spec file moved after code generation; running in CI where artifacts are in a different path; permission-restricted files.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- failed to read gRPC API Configuration description from %q: %
- failed to read OpenAPI Configuration description from %q: %w
- empty MIME type
- not match to the path pattern
- invalid pattern
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/578243aab55212bd.
Report an issue: GitHub.