router-for-me/CLIProxyAPI · error

failed to write token to file: %w

Error message

failed to write token to file: %w

What it means

Returned when json.Encoder.Encode fails while writing the merged Kimi token JSON into the opened file (token.go:108-111). Since the data was already produced by MergeMetadata (which marshals), encode failures here are almost always I/O-level: disk full mid-write, file closed prematurely, or quota exceeded.

Source

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

	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 == "" {
		return false // No expiry set, assume valid
	}
	t, err := time.Parse(time.RFC3339, ts.Expired)
	if err != nil {
		return true // Has expiry string but can't parse
	}
	// Consider expired if within refresh threshold
	return time.Now().Add(time.Duration(refreshThresholdSeconds) * time.Second).After(t)
}

// NeedsRefresh checks if the token should be refreshed.

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Free disk space on the volume holding auths/ (df -h) and retry the operation that triggered the save
  2. Check filesystem quotas if applicable (quota -s)
  3. Ensure only one process/goroutine writes the same auth file; the proxy serializes saves, so stop any external writer
  4. Re-run the Kimi login flow after freeing space so a complete token file is written
Defensive patterns

Strategy: retry

Validate before calling

var stat syscall.Statfs_t
if err := syscall.Statfs(filepath.Dir(authFilePath), &stat); err == nil && stat.Bavail == 0 {
    return fmt.Errorf("no free space on auth volume")
}

Try / catch

err := ts.SaveTokenToFile(path)
for attempt := 1; err != nil && strings.Contains(err.Error(), "failed to write token") && attempt < 3; attempt++ {
    time.Sleep(time.Duration(attempt) * 250 * time.Millisecond) // transient ENOSPC/EIO retry
    err = ts.SaveTokenToFile(path)
}

Prevention

When it happens

Trigger: ENOSPC (disk full) during the write; EDQUOT quota hit; the file was closed concurrently by another goroutine between os.Create and Encode; very rarely, an unserializable value injected into data after the merge.

Common situations: Containers with a small writable layer filling up; long-running hosts with log growth exhausting the disk; concurrent config reload triggering simultaneous token saves to the same path.

Related errors


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