{"record":{"id":"3dee8f1f5535d18d","repo":"gastownhall/beads","slug":"proxy-shutdown-publish-stop-epoch-w","errorCode":null,"errorMessage":"proxy.Shutdown: publish stop epoch: %w","messagePattern":"proxy\\.Shutdown: publish stop epoch: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/shutdown.go","lineNumber":86,"sourceCode":"\t}\n\tproxyRecoverable := shutdownErr.proxyErr == nil ||\n\t\terrors.Is(shutdownErr.proxyErr, ErrUnverifiableProcess)\n\tbackendRecoverable := shutdownErr.backendErr == nil ||\n\t\terrors.Is(shutdownErr.backendErr, ErrUnverifiableProcess)\n\treturn proxyRecoverable && backendRecoverable\n}\n\n// Shutdown stops the verified proxy and backend processes for rootDir.\n//\n// Advancing the stop epoch first makes every start attempt which began before\n// this call terminally abort instead of retrying after its child is stopped.\n// The proxy spawn marker covers the smaller release-lock-before-exec window:\n// Shutdown waits for that marked attempt to either acquire proxy.lock or fail,\n// then verifies and stops whatever it published. All waits are bounded by\n// shutdownConfirmDeadline.\nfunc Shutdown(rootDir string) error {\n\tif err := advanceStopEpoch(rootDir); err != nil {\n\t\treturn fmt.Errorf(\"proxy.Shutdown: publish stop epoch: %w\", err)\n\t}\n\n\tproxyLock, proxyErr := stopAndAcquire(\n\t\trootDir,\n\t\tLockFileName,\n\t\tPIDFileName,\n\t\tpidfile.KindProxy,\n\t\tkillRecordChecks{\n\t\t\tVerifyRoot:       true,\n\t\t\tCheckSpawnMarker: true,\n\t\t},\n\t)\n\tif proxyLock != nil {\n\t\tdefer proxyLock.Unlock()\n\t}\n\n\tbackendLock, backendErr := stopAndAcquire(\n\t\trootDir,","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/shutdown.go#L68-L104","documentation":"proxy.Shutdown begins by advancing the stop epoch so any in-flight start attempt aborts instead of republishing after the stop returns. If advanceStopEpoch fails — typically an I/O error writing the stop-epoch control file in rootDir — Shutdown returns immediately with the 'proxy.Shutdown: publish stop epoch: %w' prefix and never attempts to stop the proxy or backend.","triggerScenarios":"Calling proxy.Shutdown (or bd dolt stop) when the stop-epoch file in rootDir cannot be written: read-only rootDir, disk full, permission denied, rootDir missing, or the epoch file path is otherwise unwritable.","commonSituations":"Workspace on a read-only or full filesystem; running as a different user than the process that started the proxy; rootDir moved/renamed between start and stop; SELinux/AppArmor denying writes.","solutions":["Verify rootDir is writable by the current user and has free space (touch <rootDir>/.wtest; df -h <rootDir>).","Fix permissions/ownership on the workspace directory.","Inspect the wrapped cause after the prefix for the exact syscall error and address it.","Once writable, re-run the shutdown; the epoch will publish and stopping can proceed.","If rootDir moved, run the shutdown from the correct workspace path."],"exampleFix":"// before (shell)\nbd dolt stop   # proxy.Shutdown: publish stop epoch: open .../stop-epoch: permission denied\n// after\nsudo chown -R \"$USER\" .beads   # or move to a writable workspace\nbd dolt stop","handlingStrategy":"validation","validationCode":"info, err := os.Stat(rootDir)\nif err != nil {\n    return fmt.Errorf(\"workspace rootDir missing: %w\", err)\n}\nif f, err := os.OpenFile(filepath.Join(rootDir, \".wtest\"), os.O_CREATE|os.O_WRONLY, 0o644); err != nil {\n    return fmt.Errorf(\"cannot write stop-epoch in %s: %w\", rootDir, err)\n} else {\n    f.Close(); os.Remove(filepath.Join(rootDir, \".wtest\"))\n}","typeGuard":null,"tryCatchPattern":"if err := proxy.Shutdown(rootDir); err != nil {\n    if strings.Contains(err.Error(), \"publish stop epoch\") {\n        // fix writability/space of rootDir, then retry Shutdown\n    }\n    return err\n}","preventionTips":["Run stop from the same user/workspace that started the proxy","Ensure the workspace filesystem is mounted rw with free space","Do not move or rename rootDir while a proxy lifecycle is in flight","Check the wrapped syscall error under the prefix for the precise cause"],"tags":["go","filesystem","shutdown","stop-epoch"],"backgroundTag":"pidfile-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}