router-for-me/CLIProxyAPI · error
failed to create directory: %v
Error message
failed to create directory: %v
What it means
SaveTokenToFile creates the auth file's parent directory with 0700 before writing the persisted Claude credential. This error means os.MkdirAll failed, so the token was never written and the freshly obtained credential exists only in memory. The underlying %v tells you whether it is permissions, a path conflict, or an invalid path.
Source
Thrown at internal/auth/claude/token.go:79
}
// SaveTokenToFile serializes the Claude token storage to a JSON file.
// This method creates the necessary directory structure and writes the token
// data in JSON format to the specified file path for persistent storage.
// It merges any injected metadata into the top-level JSON object.
//
// Parameters:
// - authFilePath: The full path where the token file should be saved
//
// Returns:
// - error: An error if the operation fails, nil otherwise
func (ts *ClaudeTokenStorage) SaveTokenToFile(authFilePath string) error {
misc.LogSavingCredentials(authFilePath)
ts.Type = "claude"
// Create directory structure if it doesn't exist
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
// Merge metadata using helper
data, errMerge := misc.MergeMetadata(ts, ts.Metadata)
if errMerge != nil {
return fmt.Errorf("failed to merge metadata: %w", errMerge)
}
// Create the token file
f, err := os.Create(authFilePath)
if err != nil {
return fmt.Errorf("failed to create token file: %w", err)
}
defer func() {
if errClose := f.Close(); errClose != nil {
log.Errorf("claude token storage: close token file error: %v", errClose)
}
}()View on GitHub (pinned to 78f0c4079e)
Solutions
- Check the wrapped OS error text: 'permission denied' → fix ownership/permissions of the parent directory (chown/chmod so the runtime user can write).
- Verify auth-dir in config.yaml is a directory path that can be created (no file in its place) and is on a writable volume.
- In containers, mount a writable volume at the configured auth-dir.
- Relax SELinux/AppArmor policies if they are the denier (check audit logs).
Example fix
# before auth-dir: /var/lib/cliproxy/auths # owned by root, server runs as app user # after sudo chown -R appuser /var/lib/cliproxy auth-dir: /var/lib/cliproxy/auths
Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Dir(authFilePath)
if info, err := os.Stat(dir); err == nil && !info.IsDir() {
return fmt.Errorf("auth path %s is a file, not a directory", dir)
}
if err := os.WriteFile(filepath.Join(dir, ".probe"), nil, 0600); err != nil {
return fmt.Errorf("auth dir not writable by this user: %w", err)
} Try / catch
if err := storage.SaveTokenToFile(path); err != nil && strings.Contains(err.Error(), "failed to create directory") {
// fix ownership/permissions of auth-dir, then re-save the in-memory credential
} Prevention
- Run the process as a dedicated user that owns auth-dir.
- Mount a writable volume at auth-dir in containers; verify with a write probe at startup.
When it happens
Trigger: auth-dir (default auths/) points to a location the process cannot create or write: permission denied, a path component that is a regular file, read-only filesystem, or an unset/relative path resolving somewhere unexpected.
Common situations: Running the binary as a different user than the one that owns the auths/ directory; auth-dir misconfigured in config.yaml to a file path or a root-owned location; container images with a read-only layer where auth-dir was not mounted; SELinux/AppArmor denials on the config directory.
Related errors
- failed to create token file: %w
- failed to update source auth file: %w
- failed to write token to file: %w
- failed to create directory: %v
- failed to create token file: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/3085c010d153badd.
Report an issue: GitHub.