{"record":{"id":"4e2ad0c01c4f0cd0","repo":"flipped-aurora/gin-vue-admin","slug":"filepath-s-4e2ad0","errorCode":null,"errorMessage":"[filepath:%s]打开文件失败!","messagePattern":"\\[filepath:(.+?)\\]打开文件失败!","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/ast/interfaces_base.go","lineNumber":51,"sourceCode":"\nfunc (a *Base) Rollback(file *ast.File) error {\n\treturn nil\n}\n\nfunc (a *Base) Injection(file *ast.File) error {\n\treturn nil\n}\n\nfunc (a *Base) Format(filename string, writer io.Writer, file *ast.File) error {\n\tfileSet := a.FileSet\n\tif fileSet == nil {\n\t\tfileSet = token.NewFileSet()\n\t}\n\tif writer == nil {\n\t\topen, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666)\n\t\tdefer open.Close()\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"[filepath:%s]打开文件失败!\", filename)\n\t\t}\n\t\twriter = open\n\t}\n\terr := format.Node(writer, fileSet, file)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"[filepath:%s]注入失败!\", filename)\n\t}\n\treturn nil\n}\n\n// RelativePath 绝对路径转相对路径\nfunc (a *Base) RelativePath(filePath string) string {\n\tserver := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server)\n\thasServer := strings.Index(filePath, server)\n\tif hasServer != -1 {\n\t\tfilePath = strings.TrimPrefix(filePath, server)\n\t\tkeys := strings.Split(filePath, string(filepath.Separator))\n\t\tfilePath = path.Join(keys...)","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/ast/interfaces_base.go#L33-L69","documentation":"Base.Format writes the (modified) AST back to disk. Before format.Node runs it opens the target file with os.OpenFile(O_WRONLY|O_TRUNC); if the file cannot be opened it wraps the OS error with this message. The underlying cause (ENOENT, EACCES, EISDIR) is preserved in the wrapped chain.","triggerScenarios":"Calling Base.Format(filename, file) when the target file was deleted or moved between Parse and Format, the process lacks write permission, or filename points to a directory; also when the nil-writer branch runs against a nonexistent path.","commonSituations":"Code generator run against a stale path after a refactor; read-only checkout or mounted volume; SELinux/container permissions; file locked by another process is rare on Linux but permission issues are common in Docker builds.","solutions":["Verify the filepath exists and is writable (ls -l, touch the file as the same user)","Grant write permission: chmod/chown the file or run the generator with adequate privileges","Ensure the file is not deleted/renamed between Parse and Format in your pipeline","Re-run the generation step"],"exampleFix":"// before\nerr := base.Format(\"gen/removed.go\", file) // ENOENT\n// after\nif _, statErr := os.Stat(\"gen/removed.go\"); statErr != nil { os.Create(\"gen/removed.go\") }\nerr := base.Format(\"gen/removed.go\", file)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(filename); if err != nil || info.IsDir() { return fmt.Errorf(\"cannot format %s: %w\", filename, err) }; if info.Mode().Perm()&0200 == 0 { return fmt.Errorf(\"%s is not writable\", filename) }","typeGuard":null,"tryCatchPattern":"if err := base.Format(filename, file); err != nil { var pe *fs.PathError; if errors.As(err, &pe) { log.Printf(\"cannot open %s: %v\", filename, pe) }; return err }","preventionTips":["Never delete/rename files between Parse and Format in the same pipeline","Run generators in environments with writable source (not read-only mounts)","Check disk space before large codegen runs","Add a CI pre-check that the generated paths are writable"],"tags":["go","ast","codegen","file-io","permissions"],"backgroundTag":"file-write-permission-denied","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}