larksuite/cli · error

failed to read secret file %s: %w

Error message

failed to read secret file %s: %w

What it means

The file provider could not read the secret file at the audited secure path via vfs.ReadFile. The OS-level error (not found, permission denied, is-a-directory, I/O error) is wrapped in this message along with the audited path.

Source

Thrown at internal/binding/secret_resolve_file.go:60

		AllowReadableByOthers: false, // file provider: strict by default
		AllowSymlinkPath:      false,
	})
	if err != nil {
		return "", fmt.Errorf("file provider security audit failed: %w", err)
	}

	// Read file content
	maxBytes := pc.MaxBytes
	if maxBytes <= 0 {
		maxBytes = DefaultFileMaxBytes
	}

	// Note: vfs.ReadFile loads the entire file. maxBytes is enforced post-read
	// because vfs does not expose a size-limited reader. For secret files this
	// is acceptable (default limit 1 MiB; secrets are typically < 1 KB).
	data, err := vfs.ReadFile(securePath)
	if err != nil {
		return "", fmt.Errorf("failed to read secret file %s: %w", securePath, err)
	}

	if len(data) > maxBytes {
		return "", fmt.Errorf("file provider exceeded maxBytes (%d)", maxBytes)
	}

	content := string(data)
	mode := pc.Mode
	if mode == "" {
		mode = "json" // default mode per OpenClaw
	}

	switch mode {
	case "singleValue":
		// OpenClaw requires ref.id == SINGLE_VALUE_FILE_REF_ID for singleValue mode
		if ref.ID != SingleValueFileRefID {
			return "", fmt.Errorf("singleValue file provider expects ref id %q, got %q",
				SingleValueFileRefID, ref.ID)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Verify the path in ProviderConfig.Path exists for the user running the process: `ls -l <path>`.
  2. Check read permissions for the process user (`chmod 600` and correct ownership via chown).
  3. Confirm ~ expands to the expected home (echo $HOME) — service/systemd contexts often differ from your shell.
  4. Recreate the file if it was rotated or moved; inspect the wrapped cause for the exact OS error.

Example fix

// before
path: ~/secrets/prod.json   // file never created on this host
// after
mkdir -p ~/secrets && chmod 700 ~/secrets
printf '{"api_key":"..."}' > ~/secrets/prod.json && chmod 600 ~/secrets/prod.json
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(os.ExpandEnv(pc.Path)); err != nil {
    return fmt.Errorf("secret file not accessible before resolve: %w", err)
}

Try / catch

secret, err := resolveSecretRef(ctx, ref)
if err != nil {
    if strings.Contains(err.Error(), "failed to read secret file") {
        var perr *fs.PathError
        if errors.As(err, &perr) && errors.Is(perr, fs.ErrNotExist) {
            return fmt.Errorf("secret file missing at %s — create it or fix 'path'", perr.Path)
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling resolveSecretRef with a {source:"file"} SecretRef where the path passed the security audit but the read itself fails — file deleted between audit and read, wrong path after ~ expansion, running as a different user than the file owner, or the path is a directory.

Common situations: Typo in the configured path; the secret file was rotated/moved after config was written; `~` expansion points at a different home (e.g. under a service account or container); the file exists but the process user lacks read permission.

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 larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/24f0c76590772652. Report an issue: GitHub.