charmbracelet/crush · error
failed to persist docker mcp configuration: %w
Error message
failed to persist docker mcp configuration: %w
What it means
Wraps an error from ConfigStore.SetConfigField while persisting the staged Docker MCP config under the `mcp.docker` key of the global config file. Preparation (in-memory) succeeded, but writing the global config failed — usually a filesystem or serialization problem.
Source
Thrown at internal/config/docker_mcp.go:105
return MCPConfig{}, fmt.Errorf("docker mcp is not available, please ensure docker is installed and 'docker mcp version' succeeds")
}
mcpConfig := DockerMCPConfig()
// In-memory only; persistence happens in PersistDockerMCPConfig.
s.mutateInMemory(func(c *Config) {
if c.MCP == nil {
c.MCP = make(map[string]MCPConfig)
}
c.MCP[DockerMCPName] = mcpConfig
})
return mcpConfig, nil
}
// PersistDockerMCPConfig persists a previously prepared Docker MCP
// configuration to the global config file.
func (s *ConfigStore) PersistDockerMCPConfig(mcpConfig MCPConfig) error {
if err := s.SetConfigField(ScopeGlobal, "mcp."+DockerMCPName, mcpConfig); err != nil {
return fmt.Errorf("failed to persist docker mcp configuration: %w", err)
}
return nil
}
// EnableDockerMCP adds Docker MCP configuration and persists it.
func (s *ConfigStore) EnableDockerMCP() error {
mcpConfig, err := s.PrepareDockerMCPConfig()
if err != nil {
return err
}
if err := s.PersistDockerMCPConfig(mcpConfig); err != nil {
return err
}
return nil
}
// DisableDockerMCP removes Docker MCP configuration and persists the change.
func (s *ConfigStore) DisableDockerMCP() error {View on GitHub (pinned to 7944b8e522)
Solutions
- Check permissions on the global config file/directory (ls -l ~/.config/crush/) and fix ownership/permissions.
- Validate the existing global config file is valid JSON/TOML; fix or back it up and let Crush regenerate it.
- Free disk space if the filesystem is full.
- Retry EnableDockerMCP after fixing the filesystem; the in-memory staged config is re-prepared automatically.
Example fix
// before $ ls -l ~/.config/crush/crush.json -rw-r--r-- 1 root root ... // owned by root // after $ sudo chown -R $USER ~/.config/crush $ crush docker mcp enable
Defensive patterns
Strategy: try-catch
Validate before calling
cfgPath := globalConfigPath()
if f, err := os.OpenFile(cfgPath, os.O_WRONLY, 0o644); err != nil {
return fmt.Errorf("global config not writable (%s): %w", cfgPath, err)
} else {
f.Close()
} Type guard
func isPermissionErr(err error) bool {
return errors.Is(err, os.ErrPermission) || errors.Is(err, syscall.EACCES) || errors.Is(err, syscall.EROFS)
} Try / catch
if err := store.PersistDockerMCPConfig(mcpCfg); err != nil {
var pErr *fs.PathError
if errors.As(err, &pErr) && isPermissionErr(pErr) {
log.Fatalf("cannot write %s: check ownership (was a sudo run involved?)", pErr.Path)
}
return err
} Prevention
- Never run Crush with sudo; fix ownership of ~/.config/crush if root-owned.
- Keep the config directory writable by the current user (0o700 dir, 0o600 file).
- Monitor disk space on the home filesystem.
- Back up and validate the global config after version upgrades.
When it happens
Trigger: Calling PersistDockerMCPConfig (directly or via EnableDockerMCP) when the global config file cannot be written: read-only filesystem, permission denied on the config file/directory, corrupted config JSON, or disk full.
Common situations: ~/.config/crush/ owned by root after a sudo run; config file made read-only; home directory on a read-only mount or full disk; JSON schema mismatch after a Crush version upgrade.
Related errors
- failed to check init flag file: %w
- failed to update preferred large model: %w
- failed to update preferred small model: %w
- failed to open config file %s: %w
- failed to create parent directories: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/2525ccc3f6aea99d.
Report an issue: GitHub.