sipeed/picoclaw · error

failed to save config: %w

Error message

failed to save config: %w

What it means

The serialized document already passed schema validation, but config.SaveConfig could not persist it (helpers.go:122-124). This is a filesystem-level failure: permission denied on the config file or directory, missing parent directory, disk full, or a failed atomic temp-write/rename.

Source

Thrown at cmd/picoclaw/internal/mcp/helpers.go:123

func saveValidatedConfig(cfg *config.Config) error {
	if cfg == nil {
		return fmt.Errorf("config is nil")
	}

	normalizedCfg := normalizedConfigForSave(cfg)

	data, err := json.Marshal(normalizedCfg)
	if err != nil {
		return fmt.Errorf("failed to serialize config: %w", err)
	}

	if err := validateConfigDocument(data); err != nil {
		return err
	}

	if err := config.SaveConfig(internal.GetConfigPath(), normalizedCfg); err != nil {
		return fmt.Errorf("failed to save config: %w", err)
	}

	return nil
}

func normalizedConfigForSave(cfg *config.Config) *config.Config {
	clone := *cfg
	if cfg.Tools.MCP.Servers == nil {
		return &clone
	}

	clone.Tools = cfg.Tools
	clone.Tools.MCP = cfg.Tools.MCP
	clone.Tools.MCP.Servers = make(map[string]config.MCPServerConfig, len(cfg.Tools.MCP.Servers))
	for name, server := range cfg.Tools.MCP.Servers {
		if server.Type != "" {
			server.Type = config.NormalizeMCPTransportType(server.Type)
		}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Check permissions and ownership of the config path and its directory; fix with chown/chmod
  2. Confirm the filesystem is writable (mount | grep <path>) and disk has space (df -h)
  3. Remove stale .tmp/.bak artifacts next to the config left by an interrupted save, then retry

Example fix

# before: config owned by root after sudo use
# after
sudo chown -R "$USER" ~/.config/picoclaw
picoclaw mcp list
Defensive patterns

Strategy: try-catch

Validate before calling

cfgDir=$(dirname "$cfgPath")
[ -w "$cfgDir" ] || { echo "config dir not writable: $cfgDir" >&2; exit 1; }
df -h "$cfgDir" | tail -1   # confirm space

Try / catch

if err := saveCmd.Execute(); err != nil {
	if strings.Contains(err.Error(), "failed to save config") {
		// underlying error is wrapped; check for fs errors
		if errors.Is(err, fs.ErrPermission) {
			fmt.Fprintln(os.Stderr, "fix permissions on the config directory and retry")
		}
		if errors.Is(err, fs.ErrSpace /* or ENOSPC via syscall */) {
			fmt.Fprintln(os.Stderr, "disk full — free space and retry")
		}
	}
}

Prevention

When it happens

Trigger: Read-only $HOME or config dir; config file owned by root after earlier sudo use; disk exhausted; leftover temp file from a crashed write blocking rename.

Common situations: Running the CLI under sudo once so the config becomes root-owned; CI containers with read-only mounts; full disks in dev VMs.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/8114a39ac1750fbf. Report an issue: GitHub.