{"record":{"id":"734ef56e766adece","repo":"amir20/dozzle","slug":"failed-to-create-config-file-w","errorCode":null,"errorMessage":"failed to create config file: %w","messagePattern":"failed to create config file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/support/cli/agent_command.go","lineNumber":63,"sourceCode":"\nfunc (h *persistingNotificationHandler) GetNotificationStats() []types.SubscriptionStats {\n\treturn h.manager.GetNotificationStats()\n}\n\nfunc (h *persistingNotificationHandler) HandleNotificationConfig(subscriptions []types.SubscriptionConfig, dispatchers []types.DispatcherConfig) error {\n\t// Update the manager\n\tif err := h.manager.HandleNotificationConfig(subscriptions, dispatchers); err != nil {\n\t\treturn err\n\t}\n\n\t// Save to disk\n\tif err := os.MkdirAll(\"./data\", 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create data directory: %w\", err)\n\t}\n\n\tfile, err := os.Create(h.configPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create config file: %w\", err)\n\t}\n\tdefer file.Close()\n\n\tif err := h.manager.WriteConfig(file); err != nil {\n\t\treturn fmt.Errorf(\"failed to save config: %w\", err)\n\t}\n\n\tlog.Debug().Str(\"path\", h.configPath).Msg(\"Saved notification config to disk\")\n\treturn nil\n}\n\n// SetCloudStreamLogs applies the hub's log-streaming choice to this agent's own\n// cloud client. The agent streams its logs to cloud itself rather than through\n// the hub, so without this a user who turned streaming off saw it stop on the\n// hub while every agent kept sending.\nfunc (h *persistingNotificationHandler) SetCloudStreamLogs(enabled *bool) {\n\tcc := h.cloudConfig.Load()\n\tif cc == nil {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/support/cli/agent_command.go#L45-L81","documentation":"The handler opens h.configPath with os.Create after ensuring ./data exists. If the file cannot be created or truncated (permissions, missing parent dir, path is a directory, disk full), the error is wrapped with this message and the config is not saved.","triggerScenarios":"os.Create(h.configPath) failing: no write permission in the target directory, configPath points at an existing directory, parent directory doesn't exist (MkdirAll only covers ./data, not configPath's dir), or filesystem full/read-only.","commonSituations":"configPath set to a subdirectory like ./data/notifications/ (a dir) instead of a file; running as an unprivileged user without rights to a custom --config path; custom configPath outside ./data whose parents were never created.","solutions":["Check the wrapped syscall error (EACCES/EISDIR/ENOENT) for the cause","Ensure the parent directory of configPath exists and is writable (mkdir -p on its dir)","If configPath is a directory, change it to a file path","Run the process with a user that has write access to the path, or mount a writable volume"],"exampleFix":"// before\n--notification-config ./data/notifications/  # os.Create on a directory fails\n// after\n--notification-config ./data/notifications.yml","handlingStrategy":"try-catch","validationCode":"dir := filepath.Dir(h.configPath)\nif fi, err := os.Stat(dir); err != nil || !fi.IsDir() {\n    os.MkdirAll(dir, 0755)\n}","typeGuard":null,"tryCatchPattern":"file, err := os.Create(h.configPath)\nif err != nil {\n    if errors.Is(err, fs.ErrPermission) || errors.Is(err, syscall.EISDIR) { /* fix path/permissions */ }\n    return err\n}","preventionTips":["Point configPath at a file, never a directory","Create the parent directory of configPath, not just ./data","Ensure the process user can write to the config location"],"tags":["filesystem","permissions","persistence"],"backgroundTag":"file-open-failed","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}