googleapis/mcp-toolbox · error

failed to stat file: %w

Error message

failed to stat file: %w

What it means

This error is emitted by the `migrate` command in runMigrate when `os.Stat(filePath)` fails on a file it is about to rewrite. The command needs the file's mode bits (to preserve permissions when rewriting) and the file's existence, so a stat failure aborts processing of that one file and the error is appended to the collected error list while other files continue to be processed. Because it wraps the raw os.Stat error, the underlying cause (e.g. ENOENT, EACCES, ENOTDIR) is always included in the wrapped message.

Source

Thrown at cmd/internal/migrate/command.go:95

			continue
		}
		newBuf, err := internal.ConvertConfig(ctx, buf)
		if err != nil {
			logger.ErrorContext(ctx, err.Error())
			errs = append(errs, err)
			continue
		}
		if cmp.Equal(buf, newBuf) {
			continue
		}

		if cmd.dryRun {
			logger.DebugContext(ctx, fmt.Sprintf("printing migration to output for file: %s", filePath))
			fmt.Fprintln(opts.IOStreams.Out, string(newBuf))
		} else {
			info, err := os.Stat(filePath)
			if err != nil {
				errMsg := fmt.Errorf("failed to stat file: %w", err)
				logger.ErrorContext(ctx, errMsg.Error())
				errs = append(errs, errMsg)
				continue
			}
			backupFile := filePath + ".bak"
			err = os.Rename(filePath, backupFile)
			if err != nil {
				errMsg := fmt.Errorf("failed to rename file: %w", err)
				logger.ErrorContext(ctx, errMsg.Error())
				errs = append(errs, errMsg)
				continue
			}
			logger.DebugContext(ctx, fmt.Sprintf("successfully renamed %s to %s", filePath, backupFile))

			// set the permission to the original file's permission.
			err = os.WriteFile(filePath, newBuf, info.Mode().Perm())
			if err != nil {
				errMsg := fmt.Errorf("failed to write to file: %w", err)

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Verify the path exists with `ls -l <filePath>` and fix any typo in the configured path/glob.
  2. Check permissions on the file's parent directory (the process needs +x on it); run as a user with access or chown/chmod the path.
  3. If it is a broken symlink, remove it or recreate it pointing at a real file.
  4. Re-run migrate; the error is per-file and the command continues with other files, so inspect the full error list and fix each offending path.
  5. Check that no concurrent process deleted or moved the file during the run.

Example fix

// before: path comes from a possibly stale config
filePath := cfg.OldPath
info, err := os.Stat(filePath) // ENOENT
// after: resolve and validate the path up front
if resolved, err := filepath.EvalSymlinks(cfg.OldPath); err == nil {
    filePath = resolved
} else {
    log.Fatalf("migrate input %q does not exist: %v", cfg.OldPath, err)
}
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(filePath)
if err != nil {
    return fmt.Errorf("skipping %s: not statable (%v); check path and permissions", filePath, err)
}
if info.IsDir() {
    return fmt.Errorf("%s is a directory, expected a file", filePath)
}

Prevention

When it happens

Trigger: Running the migrate command with a path that does not exist, points to a broken symlink, or lives in a directory the process cannot traverse. Also occurs when the file is deleted between path collection and the stat call, or the path is too long / is a non-directory component (ENOTDIR).

Common situations: Typo in a file path or glob expansion produced no/stale paths; running the toolbox from a container or service account whose user lacks execute permission on the parent directory; a broken symlink left behind after an aborted run; an interrupted previous migrate run removed the file but left the .bak.

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


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/fedffbebcbb84b4d. Report an issue: GitHub.