{"record":{"id":"96a480dd9c7c0ba6","repo":"microsoft/typescript-go","slug":"invalid-handle-w","errorCode":null,"errorMessage":"invalid handle: %w","messagePattern":"invalid handle: %w","errorType":"exception","errorClass":"dirWatchError","httpStatus":null,"severity":"error","filePath":"internal/fswatch/windows.go","lineNumber":178,"sourceCode":"\tevent      windows.Handle\n}\n\nfunc newWindowsSubscription(watcherImpl *windowsBackend, w *dirWatch) (*windowsSubscription, error) {\n\tpathPtr, err := windows.UTF16PtrFromString(w.physicalDir)\n\tif err != nil {\n\t\treturn nil, &dirWatchError{err: err, dirWatch: w}\n\t}\n\th, err := windows.CreateFile(\n\t\tpathPtr,\n\t\twindows.FILE_LIST_DIRECTORY,\n\t\twindows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,\n\t\tnil,\n\t\twindows.OPEN_EXISTING,\n\t\twindows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED,\n\t\t0,\n\t)\n\tif err != nil {\n\t\treturn nil, &dirWatchError{err: fmt.Errorf(\"invalid handle: %w\", err), dirWatch: w}\n\t}\n\tvar info windows.ByHandleFileInformation\n\tif err := windows.GetFileInformationByHandle(h, &info); err != nil {\n\t\t_ = windows.CloseHandle(h)\n\t\treturn nil, &dirWatchError{err: errGetFileInfo, dirWatch: w}\n\t}\n\tif info.FileAttributes&windows.FILE_ATTRIBUTE_DIRECTORY == 0 {\n\t\t_ = windows.CloseHandle(h)\n\t\treturn nil, &dirWatchError{err: syscall.ENOTDIR, dirWatch: w}\n\t}\n\treturn &windowsSubscription{\n\t\twatcherImpl: watcherImpl,\n\t\tdirWatch:    w,\n\n\t\thandle:   h,\n\t\tstopCh:   make(chan struct{}),\n\t\tdoneCh:   make(chan struct{}),\n\t\tbufBytes: defaultBufSize,","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/fswatch/windows.go#L160-L196","documentation":"Synchronous error from WatchDirectory on Windows: CreateFile failed to open the watched directory with FILE_LIST_DIRECTORY access and FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED. The %w wraps the Win32 error (a windows.Errno), so errors.Is works against specific codes. Note the separate returns next to it: a successful open whose GetFileInformationByHandle fails yields errGetFileInfo, and a non-directory yields syscall.ENOTDIR.","triggerScenarios":"Directory does not exist (ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND), for example deleted between your existence check and the subscribe call. Access denied (ERROR_ACCESS_DENIED) because the process lacks list-directory rights. Sharing or path problems on unusual reparse points.","commonSituations":"Watching a directory a build step just removed. Unprivileged service accounts watching protected paths. Misconfigured paths, including typos and wrong case on case-sensitive volumes.","solutions":["Verify with os.Stat(dir) that the path exists and is a directory immediately before subscribing","Fix ACLs or run under an account with list-directory rights on the target","Retry in a loop when the directory is created asynchronously at startup","Branch on the wrapped errno: errors.Is(err, windows.ERROR_FILE_NOT_FOUND) suggests a race, ERROR_ACCESS_DENIED suggests permissions"],"exampleFix":"// before\nwatch, err := fswatch.Windows().WatchDirectory(dir, cb)\nif err != nil {\n    return err // no retry: races with dir creation fail permanently\n}\n\n// after\nvar watch fswatch.Watch\nfor attempt := 0; attempt < 10; attempt++ {\n    var err error\n    watch, err = fswatch.Windows().WatchDirectory(dir, cb)\n    if err == nil {\n        break\n    }\n    if !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) && !errors.Is(err, windows.ERROR_PATH_NOT_FOUND) {\n        return err\n    }\n    time.Sleep(100 * time.Millisecond)\n}","handlingStrategy":"try-catch","validationCode":"info, err := os.Stat(dir)\nif err != nil {\n    return err\n}\nif !info.IsDir() {\n    return syscall.ENOTDIR\n}","typeGuard":null,"tryCatchPattern":"watch, err := fswatch.Windows().WatchDirectory(dir, cb)\nif err != nil {\n    switch {\n    case errors.Is(err, windows.ERROR_FILE_NOT_FOUND), errors.Is(err, windows.ERROR_PATH_NOT_FOUND):\n        // race with deletion/creation: retry\n    case errors.Is(err, windows.ERROR_ACCESS_DENIED):\n        // fix ACLs or account\n    default:\n        return err\n    }\n}","preventionTips":["Stat the directory immediately before subscribing","Run the watcher under an account with list-directory rights","Retry around startup races when directories are created asynchronously","Keep watch roots off paths that build steps delete and recreate"],"tags":["windows","permissions","handle","race-condition"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}