{"record":{"id":"92d60c63e65c45f7","repo":"coreybutler/nvm-windows","slug":"failed-to-get-source-file-info-v","errorCode":null,"errorMessage":"failed to get source file info: %v","messagePattern":"failed to get source file info: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/upgrade/upgrade.go","lineNumber":944,"sourceCode":"\tdefer sourceFile.Close()\n\n\t// Create the destination file\n\tdestinationFile, err := os.Create(dst)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination file: %v\", err)\n\t}\n\tdefer destinationFile.Close()\n\n\t// Copy contents from the source file to the destination file\n\t_, err = io.Copy(destinationFile, sourceFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to copy file: %v\", err)\n\t}\n\n\t// Optionally, copy file permissions (this can be skipped if not needed)\n\tsourceInfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get source file info: %v\", err)\n\t}\n\n\terr = os.Chmod(dst, sourceInfo.Mode())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set file permissions: %v\", err)\n\t}\n\n\treturn nil\n}\n\n// copyDirContents copies all the contents (files and subdirectories) of a source directory to a destination directory.\nfunc copyDirContents(srcDir, dstDir string) error {\n\t// Ensure destination directory exists\n\terr := os.MkdirAll(dstDir, 0755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination directory %s: %v\", dstDir, err)\n\t}\n","sourceCodeStart":926,"sourceCodeEnd":962,"githubUrl":"https://github.com/coreybutler/nvm-windows/blob/5b18223ca19ff50d707f35410dbc6bd440a9f74d/src/upgrade/upgrade.go#L926-L962","documentation":"After copying bytes, copyFile stats the source file to mirror its permissions. This error means os.Stat(src) failed at that point — the source vanished or became inaccessible between the copy (which succeeded through its open handle) and the stat call. It is a TOCTOU-style race, usually triggered by antivirus quarantine or a concurrent cleanup of the temp tree.","triggerScenarios":"AV deleting the source file microseconds after io.Copy finishes; another process pruning the nvm-upgrade-* temp dir mid-upgrade; source on removable/network media disconnected mid-operation.","commonSituations":"Aggressive real-time scanners treating just-downloaded exes as threats; temp-cleaner utilities; very slow disks making the race window wide.","solutions":["Add antivirus exclusions for the nvm temp pattern and NVM_HOME, then retry.","Close temp-cleanup tools during the upgrade.","Stat once before the copy and reuse that FileInfo instead of re-statting (code-level fix).","Retry — if the file survived this time the stat succeeds."],"exampleFix":"// before\n_, err = io.Copy(destinationFile, sourceFile)\n...\nsourceInfo, err := os.Stat(src)\n\n// after: capture the stat for free from the open handle\nsourceInfo, err := sourceFile.Stat()\nif err != nil {\n    return fmt.Errorf(\"failed to get source file info: %v\", err)\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"Treat as non-fatal: the copy already succeeded, so log the stat failure, keep default permissions on the destination, and continue the upgrade.","preventionTips":["Use the already-open file handle (sourceFile.Stat()) instead of re-statting by name.","Exclude nvm temp dirs from AV to avoid the delete-after-copy race."],"tags":["filesystem","race-condition","antivirus"],"backgroundTag":null,"analyzedSha":"5b18223ca19ff50d707f35410dbc6bd440a9f74d","analyzedAt":"2026-08-15T10:06:51.428Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}