{"record":{"id":"e863faea70629bd2","repo":"anomalyco/sst","slug":"failed-to-delete-s-v","errorCode":null,"errorMessage":"failed to delete %s: %v","messagePattern":"failed to delete (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/runtime/python/python.go","lineNumber":318,"sourceCode":"\t\t\t}\n\n\t\t\tif !info.IsDir() && strings.HasSuffix(path, \".py\") {\n\t\t\t\tartifactFiles[relPath] = info\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to scan artifact files: %v\", err)\n\t\t}\n\t}\n\n\t// Delete files in artifacts that no longer exist in source\n\tfor relPath := range artifactFiles {\n\t\tif _, exists := sourceFiles[relPath]; !exists {\n\t\t\tartifactPath := filepath.Join(destDir, relPath)\n\t\t\tif err := os.Remove(artifactPath); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to delete %s: %v\", artifactPath, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Copy/update changed files\n\tfor relPath, sourceInfo := range sourceFiles {\n\t\tsourcePath := filepath.Join(srcDir, relPath)\n\t\tartifactPath := filepath.Join(destDir, relPath)\n\n\t\tneedsCopy := true\n\t\tif artifactInfo, exists := artifactFiles[relPath]; exists {\n\t\t\tif !sourceInfo.ModTime().After(artifactInfo.ModTime()) {\n\t\t\t\tneedsCopy = false\n\t\t\t}\n\t\t}\n\n\t\tif needsCopy {\n\t\t\tif err := os.MkdirAll(filepath.Dir(artifactPath), 0755); err != nil {","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/runtime/python/python.go#L300-L336","documentation":"Raised in syncPythonFiles (pkg/runtime/python/python.go:318) when os.Remove cannot delete a stale .py file that exists in the artifact directory but no longer in the source tree (for legacy workspace layouts). Typical causes: read-only artifact files/dirs, the file having been recreated as a directory, or permission mismatch.","triggerScenarios":"Source .py file renamed/deleted while the artifact copy is read-only or owned by another user; artifact path now a non-empty directory; artifact filesystem mounted read-only.","commonSituations":"Renaming a module in a workspace-layout Python project while artifacts were built by a container/CI as root; NFS/bind-mounted artifact dirs with root-squash; git checkout leaving stale artifacts with different permissions.","solutions":["Read the failing artifactPath in the message and `rm -f` it manually with sufficient privileges (or chown the artifact dir to your user)","If it became a directory, remove it with `rm -rf`; restart `sst dev` so sync reruns","If artifacts are root-owned from container builds, run `sudo chown -R $(whoami) .sst` or build as the same user"],"exampleFix":"// before\nfailed to delete .sst/artifacts/old_module.py: permission denied\n// after\nsudo rm -f .sst/artifacts/old_module.py\nsst dev","handlingStrategy":"try-catch","validationCode":"// pre-check stale artifact file is removable\nartifactPath := filepath.Join(destDir, relPath)\nif _, err := os.Lstat(artifactPath); err == nil {\n    if err := os.Chmod(artifactPath, 0644); err != nil { /* surface unwritable artifact early */ }\n}","typeGuard":null,"tryCatchPattern":"if err := syncPythonFiles(src, dst); err != nil {\n    var pe *fs.PathError\n    if strings.Contains(err.Error(), \"failed to delete\") && errors.As(err, &pe) {\n        os.RemoveAll(pe.Path) // force-remove with elevated/user perms\n        return syncPythonFiles(src, dst)\n    }\n    return err\n}","preventionTips":["After container-built artifacts, chown .sst to your user before dev","Rename source modules via git mv and restart dev rather than editing artifacts in place","Keep artifact dirs writable (0755 dirs, 0644 files)","Never mount .sst read-only for dev workflows"],"tags":["filesystem","delete","permissions","sync","python"],"backgroundTag":"permission-denied","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}