{"record":{"id":"7de5d306c627fd18","repo":"JuliusBrussee/caveman","slug":"native-runtime-chmod-socket-w","errorCode":null,"errorMessage":"native runtime chmod socket: %w","messagePattern":"native runtime chmod socket: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/nativeruntime/server_unix.go","lineNumber":61,"sourceCode":"\t\tconn, dialErr := net.DialTimeout(\"unix\", path, 50*time.Millisecond)\n\t\tif dialErr == nil {\n\t\t\t_ = conn.Close()\n\t\t\treturn errors.New(\"native runtime: socket already active\")\n\t\t}\n\t\tif err := os.Remove(path); err != nil {\n\t\t\treturn fmt.Errorf(\"native runtime remove stale socket: %w\", err)\n\t\t}\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"native runtime inspect socket: %w\", err)\n\t}\n\tlistener, err := net.Listen(\"unix\", path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"native runtime listen: %w\", err)\n\t}\n\tdefer listener.Close()\n\tdefer os.Remove(path)\n\tif err := os.Chmod(path, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"native runtime chmod socket: %w\", err)\n\t}\n\tgo func() {\n\t\t<-ctx.Done()\n\t\t_ = listener.Close()\n\t}()\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"native runtime accept: %w\", err)\n\t\t}\n\t\tgo serveConn(ctx, conn, runtime)\n\t}\n}\n","sourceCodeStart":43,"sourceCodeEnd":78,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/nativeruntime/server_unix.go#L43-L78","documentation":"Returned when os.Chmod(path, 0o600) fails on the freshly created Unix socket. The listener already bound successfully; this step locks the socket to the owning user. It fails when the socket file disappears between listen and chmod or the kernel/FS rejects mode changes on sockets.","triggerScenarios":"Calling Serve on Unix where another process (a concurrent instance or an aggressive cleaner) unlinks the socket file in the window between net.Listen and os.Chmod, or the socket lives on a filesystem that refuses chmod on socket inodes (some FUSE/network mounts).","commonSituations":"Two caveman instances racing on the same home — the loser's stale-socket cleanup removed the winner's new socket; tmp-reaper daemons deleting files in the home directory; home on NFS or a container volume with restricted ioctl support.","solutions":["Ensure only one instance serves a given home directory at a time (single-flight lock or distinct homes)","Move the socket onto a normal local filesystem (not NFS/FUSE) by relocating home","If a cleanup daemon is deleting the socket, exclude the socket path from it","Retry Serve after confirming the socket path is stable"],"exampleFix":"// before: two goroutines/processes sharing one home — loser's stale-cleanup unlinks winner's socket\nerr := nativeruntime.Serve(ctx, sharedHome, rt)\n\n// after: serialize startup per home (e.g. a lock file) so only one server owns the socket path\nif err := acquireHomeLock(sharedHome); err != nil { return err }\nerr := nativeruntime.Serve(ctx, sharedHome, rt)","handlingStrategy":"validation","validationCode":"// Ensure exclusive ownership of the socket path before Serve\nif conn, err := net.Dial(\"unix\", sockPath); err == nil {\n    _ = conn.Close()\n    return errors.New(\"socket in use; another instance is running\")\n}","typeGuard":null,"tryCatchPattern":"if err := nativeruntime.Serve(ctx, home, rt); err != nil {\n    if errors.Is(err, syscall.ENOENT) && strings.Contains(err.Error(), \"chmod socket\") {\n        // socket vanished between listen and chmod — retry once after a beat\n        time.Sleep(100 * time.Millisecond)\n        err = nativeruntime.Serve(ctx, home, rt)\n    }\n}","preventionTips":["Serialize startup with a lock file per home so concurrent cleans cannot race the chmod","Exclude the socket directory from tmp-cleaners and backup agents","Keep home on a local filesystem (ext4/apfs), not NFS/FUSE mounts"],"tags":["unix-socket","permissions","race-condition","nativeruntime"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}