{"record":{"id":"39fae7a6cafa1075","repo":"gastownhall/beads","slug":"server-doltserver-stop-close-log-w","errorCode":null,"errorMessage":"server: DoltServer.Stop: close log: %w","messagePattern":"server: DoltServer\\.Stop: close log: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/dbproxy/server/doltserver.go","lineNumber":374,"sourceCode":"\t}\n\tvar waitErr error\n\tif s.eg != nil {\n\t\twaitErr = s.eg.Wait()\n\t\tvar exitErr *exec.ExitError\n\t\tif errors.As(waitErr, &exitErr) || errors.Is(waitErr, context.Canceled) {\n\t\t\twaitErr = nil\n\t\t}\n\t}\n\tif waitErr != nil {\n\t\twaitErr = fmt.Errorf(\"server: DoltServer.Stop: %w\", waitErr)\n\t}\n\tvar closeErr error\n\tif s.logFile != nil {\n\t\tcloseErr = s.logFile.Close()\n\t\ts.logFile = nil\n\t}\n\tif closeErr != nil {\n\t\tcloseErr = fmt.Errorf(\"server: DoltServer.Stop: close log: %w\", closeErr)\n\t}\n\tvar rmErr error\n\tif s.pid != 0 {\n\t\trmErr = pidfile.Remove(s.rootDir, PIDFileName)\n\t\ts.pid = 0\n\t}\n\tif rmErr != nil {\n\t\trmErr = fmt.Errorf(\"server: DoltServer.Stop: remove pidfile: %w\", rmErr)\n\t}\n\treturn errors.Join(gcErr, waitErr, closeErr, rmErr)\n}\n\nfunc (s *DoltServer) runShutdownGC(ctx context.Context) (retErr error) {\n\tif s.database == \"\" || !s.Running(ctx) {\n\t\treturn nil\n\t}\n\tdb, err := sql.Open(\"mysql\", s.DSN(ctx, s.database, \"root\", \"\"))\n\tif err != nil {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/server/doltserver.go#L356-L392","documentation":"During Stop(), closing the server's log file (opened by NewDoltServer) failed. The error is joined into Stop's aggregate return, so shutdown still completes; it usually indicates an fd or filesystem-level problem at close time.","triggerScenarios":"s.logFile.Close() returns an error — typically deferred I/O flush failure on a full/unavailable filesystem, an already-closed fd (double Stop), or the log file was removed/changed underneath the process.","commonSituations":"Disk full so buffered writes fail on close; log file on a network mount that dropped; Stop called twice (second call has nil logFile so normally skipped, but concurrent calls can double-close).","solutions":["Check disk space and filesystem health on the volume holding the log file.","Ensure Stop is called only once; use sync.Once to guard shutdown paths.","Verify the log file path is on reliable local storage, not a flaky network mount.","Treat as non-fatal if the dolt child exited cleanly — data integrity is unaffected by log-close failure."],"exampleFix":"// before\nsrv.Stop(ctx)\nsrv.Stop(ctx) // second call can hit closed log file / fd errors\n// after\nvar stopOnce sync.Once\nstopOnce.Do(func() { _ = srv.Stop(ctx) })","handlingStrategy":"try-catch","validationCode":"// Ensure the log directory has space and is on a reliable filesystem.\nvar st syscall.Statfs_t\nif err := syscall.Statfs(filepath.Dir(logPath), &st); err == nil && st.Bavail*uint64(st.Bsize) < 5<<20 {\n    return fmt.Errorf(\"low disk on log volume; log close may fail\")\n}","typeGuard":null,"tryCatchPattern":"if err := srv.Stop(ctx); err != nil {\n    if strings.Contains(err.Error(), \"close log\") {\n        log.Warnf(\"non-fatal: log file close failed: %v\", err)\n        return nil\n    }\n    return err\n}","preventionTips":["Call Stop exactly once (sync.Once) to avoid double-close of the log fd.","Keep logs on local disk with adequate free space.","Monitor disk usage of server log files and rotate them.","Treat log-close errors as non-fatal; the server data state is unaffected."],"tags":["filesystem","shutdown","logging"],"backgroundTag":"file-close-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}