vitessio/vitess · error

cannot read file %v: %v

Error message

cannot read file %v: %v

What it means

When the -file flag form is used, getFileParam reads the file with os.ReadFile; if that read fails (missing file, permissions, directory instead of file), the underlying OS error is wrapped with the file path and returned.

Source

Thrown at go/vt/vtctl/vtctl.go:824

	return fmt.Sprintf("%v %v %v %v %v %v %v %v", topoproto.TabletAliasString(ti.Alias), keyspace, shard, topoproto.TabletTypeLString(ti.Type), ti.Addr(), ti.MysqlAddr(), fmtMapAwkable(ti.Tags), mtst)
}

// getFileParam returns a string containing either flag is not "",
// or the content of the file named flagFile
func getFileParam(flag, flagFile, name string) (string, error) {
	if flag != "" {
		if flagFile != "" {
			return "", fmt.Errorf("action requires only one of %v or %v-file", name, name)
		}
		return flag, nil
	}

	if flagFile == "" {
		return "", fmt.Errorf("action requires one of %v or %v-file", name, name)
	}
	data, err := os.ReadFile(flagFile)
	if err != nil {
		return "", fmt.Errorf("cannot read file %v: %v", flagFile, err)
	}
	return string(data), nil
}

// keyspaceParamsToKeyspaces builds a list of keyspaces.
// It supports topology-based wildcards, and plain wildcards.
// For instance:
// us*                             // using plain matching
// *                               // using plain matching
func keyspaceParamsToKeyspaces(ctx context.Context, wr *wrangler.Wrangler, params []string) ([]string, error) {
	result := make([]string, 0, len(params))
	for _, param := range params {
		if len(param) == 0 {
			return nil, errors.New("empty keyspace param in list")
		}
		if param[0] == '/' {
			// this is a topology-specific path
			result = append(result, params...)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Verify the file exists at the exact path: check with `ls -l <path>` from the same working directory used for the command.
  2. Fix permissions so the vtctl process user can read the file.
  3. Use an absolute path in scripts and CI to avoid working-directory mismatches.
  4. Confirm volume mounts/configmaps actually contain the file when running in containers.

Example fix

// before
ApplySchema --sql-file ./schem.sql commerce   // typo, file missing
// after
ApplySchema --sql-file /etc/vitess/schema.sql commerce
Defensive patterns

Strategy: validation

Validate before calling

const fs = require("fs")
fs.accessSync(sqlFilePath, fs.constants.R_OK) // throws early if missing/unreadable

Type guard

function fileReadable(p) {
  try { require("fs").accessSync(p, require("fs").constants.R_OK); return true } catch { return false }
}

Try / catch

try {
  run(`vtctlclient ApplySchema --sql-file ${p} commerce`)
} catch (e) {
  if (String(e).includes("cannot read file")) {
    console.error(`File unreadable: ${p}. Check existence, permissions, and cwd.`)
  } else throw e
}

Prevention

When it happens

Trigger: Running ApplySchema with `--sql-file <path>` (or analogous -file flag) where the path does not exist, is unreadable by the vtctl process user, or points to a directory.

Common situations: Relative path resolved from a different working directory in scripts/CI; file created by another user with restrictive permissions; typo in the filename; container image missing the mounted schema file.

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 vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/54fe7b3abbf6e8de. Report an issue: GitHub.