larksuite/cli · error
failed to read secret file %s: %w
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.).
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)
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 {source, id} object
- unknown secret source: %s
- keychain unavailable: %w hint: use file: reference in config
- create lock dir: %w
- read content-safety config: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/4013b4f09fb64d37.
Report an issue: GitHub.