router-for-me/CLIProxyAPI · error

failed to create token file: %w

Error message

failed to create token file: %w

What it means

Returned by KimiTokenStorage.SaveTokenToFile when os.Create fails on the target auth file path (token.go:98-100). The directory was created successfully just before, so the failure is specific to the file itself: permission denied on the directory, path is a directory, name too long, or disk full.

Source

Thrown at internal/auth/kimi/token.go:99

// SaveTokenToFile serializes the Kimi token storage to a JSON file.
func (ts *KimiTokenStorage) SaveTokenToFile(authFilePath string) error {
	misc.LogSavingCredentials(authFilePath)
	ts.Type = "kimi"

	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)
	}

	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("kimi token storage: close token file error: %v", errClose)
		}
	}()

	encoder := json.NewEncoder(f)
	encoder.SetIndent("", "  ")
	if err = encoder.Encode(data); err != nil {
		return fmt.Errorf("failed to write token to file: %w", err)
	}
	return nil
}

// IsExpired checks if the token has expired.
func (ts *KimiTokenStorage) IsExpired() bool {
	if ts.Expired == "" {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Verify the path is a regular file location, not an existing directory: ls -la auths/
  2. Grant write permission on the directory to the running user: chown -R appuser: auths/ && chmod 700 auths/
  3. Check disk space and inodes: df -h && df -i
  4. On SELinux systems, check the audit log and fix the context of the auths directory

Example fix

# before
ls -la auths/   # shows kimi-token.json is a DIRECTORY
# after
rm -rf auths/kimi-token.json   # remove the directory so os.Create can make a file
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(authFilePath); err == nil && info.IsDir() {
    return fmt.Errorf("auth file path %q is a directory", authFilePath)
}

Try / catch

if err := ts.SaveTokenToFile(path); err != nil {
    if strings.Contains(err.Error(), "failed to create token file") {
        log.Errorf("cannot write %s: check perms/disk", path)
    }
    return err
}

Prevention

When it happens

Trigger: authFilePath points at an existing directory; the parent directory exists but lacks write permission for the process user; filesystem out of space or inodes; the file already exists as an immutable/locked file.

Common situations: auths/ owned by root while the proxy runs unprivileged; a leftover directory named like the target token file (e.g. auths/kimi-xxx.json/); full disk in a container; SELinux denying creation.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/d6ade0f143cba50b. Report an issue: GitHub.