Preflight Checklist

  • [x] I have searched the issue tracker for an issue that matches the one I want to file, without success.
  • [x] I am not looking for support or already pursued the available support channels without success.
  • [x] I have checked the troubleshooting guide for my problem, without success.

Viper Version

1.20.1

Go Version

1.24.5

Config Source

Files

Format

JSON

Repl.it link

No response

Code reproducing the issue


Expected Behavior

A method should be available to manually stop WatchConfig().

Actual Behavior

There are no publicly available methods to stop WatchConfig().

Currently, it seems that memory is only released by the GC when both of the following conditions are met:

  1. The Goroutine has exited:

    • The configuration file is deleted (triggering an fsnotify.Remove event).
    • Or an error occurs in the watcher (the watcher.Errors channel is closed or returns an error).
    • At this point, the goroutine executes eventsWG.Done() and exits, releasing its reference to v.
  2. No other external references exist:

    • Apart from this goroutine, no other code holds a reference to v (e.g., the caller has set v to nil or no longer uses it).

Steps To Reproduce

No response

Additional Information

Many times, it's necessary to dynamically switch between different configuration files for corresponding settings. While a UI is provided to trigger this (requiring SSE pushes via WatchConfig), the process of switching cannot completely clear the resource usage of the old WatchConfig, leading to leakage issues.

Comment From: LuSrackhall

Fix Recommendations (Preventing Memory Leaks)

To proactively release memory, add an explicit stop mechanism (e.g., include a stop channel in the Viper struct):

```go type Viper struct { // Add a stop channel stopCh chan struct{} }

func (v *Viper) WatchConfig() { // ...existing code... go func() { defer eventsWG.Done() for { select { case <-v.stopCh: // Listen for stop signal return case event, ok := <-watcher.Events: // ...handle events... case err, ok := <-watcher.Errors: // ...handle errors... } } }() }

// Add a stop method func (v *Viper) StopWatch() { close(v.stopCh) // Close the channel to trigger goroutine exit }

Comment From: LuSrackhall

If possible, I would be happy to submit a PR for the related issue.