{"record":{"id":"30c1391f484ae93a","repo":"gastownhall/beads","slug":"ensureproxiedserverconfig-write-s-w","errorCode":null,"errorMessage":"ensureProxiedServerConfig: write %s: %w","messagePattern":"ensureProxiedServerConfig: write (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/proxied_server.go","lineNumber":158,"sourceCode":"\n\tswitch _, err := os.Stat(path); {\n\tcase err == nil:\n\t\treturn path, nil\n\tcase !os.IsNotExist(err):\n\t\treturn \"\", fmt.Errorf(\"ensureProxiedServerConfig: stat %s: %w\", path, err)\n\t}\n\n\tport, err := proxy.PickFreePort()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"ensureProxiedServerConfig: pick free port: %w\", err)\n\t}\n\n\tbody, err := renderProxiedServerConfig(port)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"ensureProxiedServerConfig: render YAML: %w\", err)\n\t}\n\tif err := atomicWriteFile(resolveConfigWriteTarget(path), body); err != nil {\n\t\treturn \"\", fmt.Errorf(\"ensureProxiedServerConfig: write %s: %w\", path, err)\n\t}\n\treturn path, nil\n}\n\n// resolveConfigWriteTarget resolves path to its physical location before\n// an atomic rewrite. os.Rename's destination argument does not follow\n// symlinks — it unlinks and replaces whatever is AT that path, symlink or\n// not — so writing straight to a symlinked config.yaml would silently\n// replace the symlink itself with a regular file instead of updating the\n// file it points at. Falls back to path unresolved when it does not exist\n// yet (filepath.EvalSymlinks errors on a missing path), which covers both\n// the ordinary \"no config.yaml yet\" case and a dangling symlink.\nfunc resolveConfigWriteTarget(path string) string {\n\tresolved, err := filepath.EvalSymlinks(path)\n\tif err != nil {\n\t\treturn path\n\t}\n\treturn resolved","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/proxied_server.go#L140-L176","documentation":"The final step of ensureProxiedServerConfig writes the rendered YAML atomically via atomicWriteFile(resolveConfigWriteTarget(path), body); any write failure is wrapped here. resolveConfigWriteTarget resolves the path to its physical location first because os.Rename's destination does not follow symlinks — so this error covers temp-file creation, write, fsync, or rename failures at the real destination.","triggerScenarios":"atomicWriteFile fails: parent dir not writable (EACCES), disk full (ENOSPC), target is inside a read-only mount or container layer, temp-file create/rename blocked by sandbox policy, or quota exceeded on the volume holding ~/.beads.","commonSituations":"Read-only container root filesystem with HOME on the overlay; full disk or user quota on $HOME; ~/.beads owned by root after a sudo run; SELinux/AppArmor denying writes; sync tools (Dropbox/NFS) holding locks or failing the rename.","solutions":["Read the wrapped errno: ENOSPC/quota → free space (`df -h <dir>`, `du -sh ~/.beads`) or raise the quota; EACCES → fix ownership of the config directory (`ls -ld ~/.beads`, `sudo chown -R $USER ~/.beads` if root-owned).","If the filesystem is read-only (container overlay, immutable mount), remount rw or relocate the config to a writable path via a custom --config.","Disable/repair interference from sync daemons: pause Dropbox/backup agents or move ~/.beads off the synced/network mount.","Check LSM denials (SELinux audit log / AppArmor) and adjust policy if writes to the path are being blocked.","Rerun the command after fixing conditions — the atomic write either succeeds fully or leaves the old file intact, so retry is safe."],"exampleFix":"// before\n$ ls -ld ~/.beads\ndrwx------ root root ~/.beads    # root-owned after sudo run\n// after\n$ sudo chown -R $USER:$USER ~/.beads && bd serve","handlingStrategy":"try-catch","validationCode":"dir := filepath.Dir(cfgPath)\nif err := unix.Access(dir, unix.W_OK); err != nil {\n\treturn fmt.Errorf(\"cannot write config dir %s: %w\", dir, err)\n}","typeGuard":null,"tryCatchPattern":"if err := atomicWriteFile(target, body); err != nil {\n\tswitch {\n\tcase errors.Is(err, fs.ErrPermission):\n\t\t// chown/chmod the beads dir or relocate config\n\tcase errors.Is(err, syscall.ENOSPC):\n\t\t// free disk space / raise quota, then retry\n\tdefault:\n\t\tlog.Printf(\"config write failed: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Never run bd under sudo in ways that leave ~/.beads root-owned; chown it back if it happens.","Keep the beads directory off read-only mounts and out of overlay filesystems in containers — mount a writable volume for it.","Monitor disk space/quota on the volume hosting ~/.beads.","Pause file-sync daemons (Dropbox, backup agents) for directories bd writes to atomically."],"tags":["go","filesystem","atomic-write","permissions","proxied-server"],"backgroundTag":"file-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}