router-for-me/CLIProxyAPI · error
pluginhost: save command-line auth %s: %w
Error message
pluginhost: save command-line auth %s: %w
What it means
This wraps a failure from tokenStore.Save while persisting one of the auths returned by a plugin command-line run; %s is the auth record's ID. The underlying error (via %w) explains the real cause — usually a filesystem problem in the auth directory or a serialization failure.
Source
Thrown at internal/pluginhost/command_line.go:387
store := sdkAuth.GetTokenStore()
if store == nil {
return nil, fmt.Errorf("pluginhost: token store unavailable")
}
summary := h.hostConfigSummary()
if summary.AuthDir != "" {
if setter, okSetter := store.(interface{ SetBaseDir(string) }); okSetter {
setter.SetBaseDir(summary.AuthDir)
}
}
savedPaths := make([]string, 0, len(auths))
for index, authData := range auths {
record := h.AuthDataToCoreAuth(authData, "", "")
if record == nil {
return savedPaths, fmt.Errorf("pluginhost: command-line auth %d is invalid", index+1)
}
savedPath, errSave := store.Save(ctx, record)
if errSave != nil {
return savedPaths, fmt.Errorf("pluginhost: save command-line auth %s: %w", record.ID, errSave)
}
if strings.TrimSpace(savedPath) != "" {
savedPaths = append(savedPaths, savedPath)
}
}
return savedPaths, nil
}
func appendCommandLineSavedPaths(stdout []byte, savedPaths []string) []byte {
if len(savedPaths) == 0 {
return stdout
}
out := append([]byte(nil), stdout...)
if len(out) > 0 && out[len(out)-1] != '\n' {
out = append(out, '\n')
}
for _, savedPath := range savedPaths {
if strings.TrimSpace(savedPath) == "" {View on GitHub (pinned to 78f0c4079e)
Solutions
- Read the wrapped error — it distinguishes permission problems from encoding problems.
- Verify the auth directory (config auth-dir, default auths/) exists and is writable by the process user: ls -ld and touch a test file.
- Fix ownership/permissions (chown/chmod) or mount a writable volume in containers.
- Re-run the command-line flow; note earlier auths may already be saved, so clean duplicates if the plugin cannot resume.
Example fix
# before: auth dir not writable $ ls -ld /etc/cliproxy/auths drwxr-xr-x 2 root root ... /etc/cliproxy/auths # after $ sudo chown -R cliproxy:cliproxy /etc/cliproxy/auths $ ./cli-proxy-api <plugin command-line flow>
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight the auth directory
info, err := os.Stat(authDir)
if err != nil || !info.IsDir() {
return fmt.Errorf("auth dir %s missing", authDir)
}
if err := os.WriteFile(filepath.Join(authDir, ".write-check"), nil, 0o600); err != nil {
return fmt.Errorf("auth dir %s not writable", authDir)
} Type guard
func authDirWritable(dir string) bool {
f, err := os.CreateTemp(dir, ".probe-*")
if err != nil {
return false
}
f.Close()
os.Remove(f.Name())
return true
} Try / catch
paths, err := host.PersistCommandLineAuths(ctx, auths)
if err != nil && strings.Contains(err.Error(), "save command-line auth") {
cause := errors.Unwrap(err) // real filesystem error
if os.IsPermission(cause) {
return fixPermissionsAndRetry(authDir, auths)
}
return err
} Prevention
- Health-check the auth directory writability at startup, not at first save.
- Run the service under a user that owns the auth directory.
- Mount auth volumes read-write in containers.
When it happens
Trigger: store.Save failing: auth directory not writable or missing, disk full, path permission issues, or the store rejecting the record. Entries before this one were already saved, so the batch is partially applied.
Common situations: Running the service with an auths/ directory owned by another user; containers with read-only volumes for auth data; auth dir relocated via config to a path that does not exist; SELinux/AppArmor denying writes.
Related errors
- failed to read auth dir: %w
- failed to read auth file: %w
- failed to write auth file: %w
- failed to update source auth file: %w
- failed to create directory: %v
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/c9a19aad0914235d.
Report an issue: GitHub.