{"record":{"id":"ae7c52ce9196aa9b","repo":"commaai/openpilot","slug":"failed-to-save-cabana-settings-to-s-s","errorCode":null,"errorMessage":"failed to save Cabana settings to %s: %s\n","messagePattern":"failed to save Cabana settings to (.+?): (.+?)\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"openpilot/tools/cabana/settings.cc","lineNumber":132,"sourceCode":"  if (fd < 0) {\n    fprintf(stderr, \"failed to create temporary Cabana settings %s: %s\\n\", temporary_path.c_str(), strerror(errno));\n    return false;\n  }\n\n  bool success = writeAll(fd, contents) && fsync(fd) == 0;\n  if (close(fd) < 0) success = false;\n  if (success && rename(temporary_path.c_str(), path.c_str()) < 0) success = false;\n\n  if (success) {\n    int dir_fd = open(path.parent_path().c_str(), O_RDONLY | O_CLOEXEC);\n    success = dir_fd >= 0 && fsync(dir_fd) == 0;\n    if (dir_fd >= 0 && close(dir_fd) < 0) success = false;\n  }\n\n  if (!success) {\n    const int saved_errno = errno;\n    unlink(temporary_path.c_str());\n    fprintf(stderr, \"failed to save Cabana settings to %s: %s\\n\", path.c_str(), strerror(saved_errno));\n  }\n  return success;\n}\n\nbool preserveCorruptSettings() {\n  const auto path = settingsFile();\n  auto backup = path;\n  backup += \".corrupt\";\n  for (int i = 1; std::filesystem::exists(backup); ++i) {\n    backup = path;\n    backup += \".corrupt.\" + std::to_string(i);\n  }\n  if (rename(path.c_str(), backup.c_str()) < 0) {\n    fprintf(stderr, \"failed to preserve corrupt Cabana settings %s: %s\\n\", path.c_str(), strerror(errno));\n    return false;\n  }\n  fprintf(stderr, \"preserved corrupt Cabana settings at %s\\n\", backup.c_str());\n  return true;","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/commaai/openpilot/blob/516ec1e68203439a73f340f1d0b3b91eabc626ee/openpilot/tools/cabana/settings.cc#L114-L150","documentation":"Catch-all failure report at the end of saveSettings() in tools/cabana/settings.cc. It fires when any stage of the atomic save after temp-file creation failed: writeAll() (partial/failed write), fsync(fd), close(fd), rename(temp -> cabana.json), or the directory open/fsync that durably records the rename. The temp file is unlinked and false returned. Note the errno printed is whatever errno happened to hold, which after close/fsync failures may not identify the first failing step.","triggerScenarios":"saveSettings() where mkstemp succeeded but one of these fails: write() loop returns <= 0 (ENOSPC, EIO), fsync() != 0, close() < 0, rename() < 0 (cross-device is impossible here since temp is in the same dir, but permission/dir-removed races apply), or open(parent_path, O_RDONLY)/fsync(dir_fd) fails. Any one flips `success` to false and reaches the fprintf with saved_errno captured just before the unlink.","commonSituations":"Disk fills up mid-write (ENOSPC in writeAll — most common); flaky USB/NFS mount returning EIO on fsync; config directory deleted between ensureSettingsDirectory and rename; errno ambiguity making the message misleading after the close/fsync branch.","solutions":["Check disk space first: `df -h <configPath>` — ENOSPC during writeAll is the most frequent cause.","If strerror is 'Input/output error', suspect the underlying storage/mount (fsck, remount, replace media).","Verify the config directory still exists and is writable at save time; re-run ensureSettingsDirectory() before retrying.","Retry the save — settings are written on change, so transient failures self-heal on the next modification once space is freed."],"exampleFix":"# before\n# stderr: failed to save Cabana settings to /home/user/.comma/cabana.json: No space left on device\ndf -h /home/user        # 100% full\n\n# after\nclean up space (journal, cores, old drives), then change any setting in cabana to force a re-save","handlingStrategy":"retry","validationCode":"// Cheap pre-save check: space and writability of the target dir\n#include <sys/statvfs.h>\nbool dirReadyForAtomicSave(const std::filesystem::path &dir, size_t needed = 65536) {\n  struct statvfs st;\n  if (statvfs(dir.c_str(), &st) != 0) return false;\n  return st.f_bavail * st.f_frsize > needed && (st.f_flag & ST_RDONLY) == 0;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Monitor disk space — ENOSPC during writeAll is the top cause of this failure.","If a save fails, change any setting again after freeing space; saves are idempotent full rewrites.","On flaky storage (USB/NFS), move the config dir to local disk or fix the mount before long sessions."],"tags":["cabana","settings","fsync","rename","disk-full","atomic-write"],"backgroundTag":null,"analyzedSha":"516ec1e68203439a73f340f1d0b3b91eabc626ee","analyzedAt":"2026-08-15T00:17:37.461Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}