@@ -1833,21 +1833,31 @@ func (w *Watcher) validateJSONFile(filePath string) error {
18331833// validatePath ensures the file path is safe and within expected boundaries
18341834// It includes Windows-specific validation for drive letters, UNC paths, and edge cases
18351835func (w * Watcher ) validatePath (filePath string ) error {
1836- // Validate intent filename patterns first (before Windows validation)
1837- // This ensures consistent error messages across platforms
1838- filename := filepath .Base (filePath )
1839- if err := w .validateIntentFilename (filename ); err != nil {
1840- return err
1836+ // On Windows, null bytes in paths cause filepath operations to fail
1837+ // Check this first before any other validation
1838+ if runtime .GOOS == "windows" && strings .Contains (filePath , "\x00 " ) {
1839+ // Try to get absolute path to trigger the OS error
1840+ _ , err := filepath .Abs (filePath )
1841+ if err != nil {
1842+ return fmt .Errorf ("failed to get absolute path: %w" , err )
1843+ }
18411844 }
18421845
1843- // Import pathutil for Windows-specific validation
1844- // Perform initial Windows-specific validation
1846+ // Perform Windows-specific validation first on Windows systems
1847+ // This catches Windows path validation errors before general filename validation
18451848 if runtime .GOOS == "windows" {
18461849 if err := pathutil .ValidateWindowsPath (filePath ); err != nil {
18471850 return fmt .Errorf ("Windows path validation failed: %w" , err )
18481851 }
18491852 }
18501853
1854+ // Validate intent filename patterns (after Windows validation)
1855+ // This ensures Windows-specific errors are caught first
1856+ filename := filepath .Base (filePath )
1857+ if err := w .validateIntentFilename (filename ); err != nil {
1858+ return err
1859+ }
1860+
18511861 // Normalize path separators for consistent handling
18521862 normalizedPath := pathutil .NormalizePathSeparators (filePath )
18531863
@@ -2938,4 +2948,60 @@ func (w *Watcher) IsShutdownFailure(err error, errorMsg string) bool {
29382948 }
29392949
29402950 return false
2951+ }
2952+
2953+ // safeQueueWorkItem safely queues a work item with comprehensive checks to prevent
2954+ // "send on closed channel" panics. It checks shutdown state, context cancellation,
2955+ // and handles backpressure gracefully.
2956+ func (w * Watcher ) safeQueueWorkItem (workItem WorkItem , cancelFunc context.CancelFunc ) error {
2957+ // First check - shutdown has started
2958+ if atomic .LoadInt32 (& w .workerPool .shutdownStarted ) == 1 {
2959+ if cancelFunc != nil {
2960+ cancelFunc ()
2961+ }
2962+ return fmt .Errorf ("shutdown in progress" )
2963+ }
2964+
2965+ // Second check - context is already done
2966+ select {
2967+ case <- w .ctx .Done ():
2968+ if cancelFunc != nil {
2969+ cancelFunc ()
2970+ }
2971+ return fmt .Errorf ("context cancelled" )
2972+ default :
2973+ // Context is still active, continue
2974+ }
2975+
2976+ // Third check - try to queue with proper cancellation handling
2977+ select {
2978+ case w .workerPool .workQueue <- workItem :
2979+ // Successfully queued
2980+ return nil
2981+ case <- w .ctx .Done ():
2982+ if cancelFunc != nil {
2983+ cancelFunc ()
2984+ }
2985+ return fmt .Errorf ("context cancelled during queue" )
2986+ default :
2987+ // Work queue is full, implement backpressure
2988+ log .Printf ("LOOP:BACKPRESSURE - Work queue full, applying backpressure for %s" ,
2989+ filepath .Base (workItem .FilePath ))
2990+
2991+ // Record backpressure event
2992+ atomic .AddInt64 (& w .metrics .BackpressureEventsTotal , 1 )
2993+
2994+ // For once mode, we should retry; for regular mode, it's optional
2995+ if w .config .Once {
2996+ // Try again with exponential backoff in once mode
2997+ w .workerPool .senders .Add (1 )
2998+ go func () {
2999+ defer w .workerPool .senders .Done ()
3000+ w .retryWithBackoff (workItem , cancelFunc )
3001+ }()
3002+ return nil
3003+ }
3004+
3005+ return fmt .Errorf ("work queue full" )
3006+ }
29413007}
0 commit comments