larksuite/cli · error
failed to read secret file
Error message
failed to read secret file %s: %w
What it means
ResolveSecretInput resolves a SecretInput to the actual secret value. When the secret is a file reference, it reads the file via vfs.ReadFile and wraps any read failure with this error, including the file path and the underlying cause (missing file, permission denied, etc.).
Solutions
- Verify the file exists at the exact path in the id field (cat the path) and fix the path in config.json if it moved.
- Fix file permissions so the CLI user can read it (e.g. chmod 600 /path/to/secret).
- Store the secret in the OS keychain by re-running the CLI's config/login flow, switching to a keychain reference.
- As a temporary unblock, set the secret inline as a plain string, then move it back to a file reference.
Example fix
// before (config.json)
"appSecret": {"source": "file", "id": "/tmp/secret.txt"} // file deleted
// after
"appSecret": {"source": "file", "id": "/home/me/.lark/appsecret"} // file restored, chmod 600 Defensive patterns
Strategy: validation
Validate before calling
if ref := cfg.AppSecret.Ref; ref != nil && ref.Source == "file" {
if _, err := os.Stat(ref.ID); err != nil {
return fmt.Errorf("secret file %s unreadable: %w", ref.ID, err)
}
} Try / catch
secret, err := core.ResolveSecretInput(input, kc)
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) {
return fmt.Errorf("fix or recreate secret file %s: %w", pe.Path, err)
}
return err
} Prevention
- Store secret files in a stable location (e.g. ~/.lark/) outside tmp dirs that get cleaned.
- Mount secret files explicitly in containers and verify the paths exist there.
- Set restrictive permissions (chmod 600) and confirm readability as the user running the CLI.
- After moving machines or users, re-check every file reference in config.json.
When it happens
Trigger: Calling ResolveSecretInput (via authLogoutRun or ResolveConfigFromMulti) when the config appSecret is {"source":"file","id":"<path>"} and the referenced file cannot be read: missing, renamed, wrong permissions, or an unreadable/invalid path.
Common situations: Secret file deleted by cleanup scripts or tmp reapers; config copied to another machine where the absolute path does not exist; permissions changed so the CLI user cannot read it; containers where the host path was never mounted.
Related errors
- appSecret must be a string or
- create config dir
- create lock dir
- keychain unavailable
- read content-safety config
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/4013b4f09fb64d37.
Report an issue: GitHub.
Appendix: source
Thrown at internal/core/secret_resolve.go:30
)
const secretKeyPrefix = "appsecret:"
func secretAccountKey(appId string) string {
return secretKeyPrefix + appId
}
// ResolveSecretInput resolves a SecretInput to a plain string.
// SecretRef objects are resolved by source (file / keychain).
func ResolveSecretInput(s SecretInput, kc keychain.KeychainAccess) (string, error) {
if s.Ref == nil {
return s.Plain, nil
}
switch s.Ref.Source {
case "file":
data, err := vfs.ReadFile(s.Ref.ID)
if err != nil {
return "", fmt.Errorf("failed to read secret file %s: %w", s.Ref.ID, err)
}
return strings.TrimSpace(string(data)), nil
case "keychain":
return kc.Get(keychain.LarkCliService, s.Ref.ID)
default:
return "", fmt.Errorf("unknown secret source: %s", s.Ref.Source)
}
}
// ForStorage determines how to store a secret in config.json.
// - SecretRef → preserved as-is
// - Plain text → stored in keychain, returns keychain SecretRef
// Returns error if keychain is unavailable (no silent plaintext fallback).
func ForStorage(appId string, input SecretInput, kc keychain.KeychainAccess) (SecretInput, error) {
if !input.IsPlain() {
return input, nil // SecretRef → keep as-is
}
key := secretAccountKey(appId)View on GitHub (pinned to 7fd6ef3c07)