amir20/dozzle · error

failed to save config

Error message

failed to save config: %w

What it means

After the file is created, the manager serializes the current subscriptions and dispatchers via WriteConfig(file). Any error during that write (marshal failure inside the manager, write syscall error, disk full) is wrapped with this message; the on-disk config may be partially written.

Solutions

  1. Check the wrapped error: ENOSPC means free disk space; serialization errors mean inspect the config data
  2. Verify disk space and mount health on the data volume
  3. Delete the partial config file and retry the operation so a clean file is written
  4. Update the client/manager if the wrapped error indicates a marshal bug in WriteConfig

Example fix

// before
# disk full: WriteConfig fails mid-write, leaving truncated notifications.yml
// after
# df -h ./data && rm ./data/notifications.yml, then retry the save
Defensive patterns

Strategy: try-catch

Validate before calling

if st, err := os.Stat(filepath.Dir(h.configPath)); err != nil || st == nil {
    return fmt.Errorf("config dir missing")
}

Try / catch

if err := h.manager.WriteConfig(file); err != nil {
    file.Close()
    os.Remove(h.configPath) // drop partial write
    return err
}

Prevention

When it happens

Trigger: h.manager.WriteConfig(file) returning an error: I/O failure while writing to the just-created file (ENOSPC, EIO) or a serialization problem inside the manager (e.g. unsupported dispatcher data).

Common situations: Disk filling up mid-write; network volume dropping; a dispatcher/subscription holding data that fails to serialize into the YAML output.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/34faface5a25f1f5. Report an issue: GitHub.

Appendix: source

Thrown at internal/support/cli/agent_command.go:68

func (h *persistingNotificationHandler) HandleNotificationConfig(subscriptions []types.SubscriptionConfig, dispatchers []types.DispatcherConfig) error {
	// Update the manager
	if err := h.manager.HandleNotificationConfig(subscriptions, dispatchers); err != nil {
		return err
	}

	// Save to disk
	if err := os.MkdirAll("./data", 0755); err != nil {
		return fmt.Errorf("failed to create data directory: %w", err)
	}

	file, err := os.Create(h.configPath)
	if err != nil {
		return fmt.Errorf("failed to create config file: %w", err)
	}
	defer file.Close()

	if err := h.manager.WriteConfig(file); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	log.Debug().Str("path", h.configPath).Msg("Saved notification config to disk")
	return nil
}

// SetCloudStreamLogs applies the hub's log-streaming choice to this agent's own
// cloud client. The agent streams its logs to cloud itself rather than through
// the hub, so without this a user who turned streaming off saw it stop on the
// hub while every agent kept sending.
func (h *persistingNotificationHandler) SetCloudStreamLogs(enabled *bool) {
	cc := h.cloudConfig.Load()
	if cc == nil {
		return
	}
	updated := *cc
	updated.StreamLogs = enabled
	h.cloudConfig.Store(&updated)

View on GitHub (pinned to d9463cbe21)