cloudreve/cloudreve · critical

failed to write config file: %w

Error message

failed to write config file: %w

What it means

Thrown by NewIniConfigProvider (pkg/conf/conf.go:46) when the freshly created config file was opened but f.WriteString(confContent) fails to write the rendered default template. The file handle is valid, so this is an I/O-time failure: disk full (ENOSPC), quota exceeded, device/FS-level write errors (NFS stale handle, disk detached), or the file was opened on a filesystem that went read-only. Startup aborts because the config was never persisted.

Source

Thrown at pkg/conf/conf.go:46

// NewIniConfigProvider initializes a new Ini config file provider. A default config file
// will be created if the given path does not exist.
func NewIniConfigProvider(configPath string, l logging.Logger) (ConfigProvider, error) {
	if configPath == "" || !util.Exists(configPath) {
		l.Info("Config file %q not found, creating a new one.", configPath)
		// 创建初始配置文件
		confContent := util.Replace(map[string]string{
			"{SessionSecret}": util.RandStringRunesCrypto(64),
		}, defaultConf)
		f, err := util.CreatNestedFile(configPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create config file: %w", err)
		}

		// 写入配置文件
		_, err = f.WriteString(confContent)
		if err != nil {
			return nil, fmt.Errorf("failed to write config file: %w", err)
		}

		f.Close()
	}

	cfg, err := ini.Load(configPath, []byte(getOverrideConfFromEnv(l)))
	if err != nil {
		return nil, fmt.Errorf("failed to parse config file %q: %w", configPath, err)
	}

	provider := &iniConfigProvider{
		database:        *DatabaseConfig,
		system:          *SystemConfig,
		ssl:             *SSLConfig,
		unix:            *UnixConfig,
		slave:           *SlaveConfig,
		redis:           *RedisConfig,
		cors:            *CORSConfig,

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. Free space or raise the quota on the volume holding the config path (df -h to confirm)
  2. If the FS remounted read-only, check dmesg for disk errors, fix, and remount rw
  3. Move the config path to a reliably writable local volume
  4. For containers, increase ephemeral-storage limits or write config to a mounted volume

Example fix

# before
$ ./cloudreve
failed to write config file: write /cloudreve/conf.ini: no space left on device

# after
$ df -h /cloudreve
$ docker system prune   # or extend the volume
$ ./cloudreve
Defensive patterns

Strategy: validation

Validate before calling

// free-space preflight for the config volume
func checkDiskSpace(dir string, need uint64) error {
    var st syscall.Statfs_t
    if err := syscall.Statfs(dir, &st); err != nil {
        return err
    }
    if uint64(st.Bavail)*uint64(st.Bsize) < need {
        return fmt.Errorf("less than %d bytes free on %s", need, dir)
    }
    return nil
}

Type guard

func isDiskFull(err error) bool {
    return err != nil && (errors.Is(err, syscall.ENOSPC) || strings.Contains(err.Error(), "no space left on device"))
}

Try / catch

provider, err := conf.NewIniConfigProvider(configPath, l)
if err != nil {
    if isDiskFull(err) {
        log.Fatalf("config volume is full; free space or extend the disk, then restart")
    }
    log.Fatalf("config write failed: %v", err)
}

Prevention

When it happens

Trigger: First-run generation of the INI file on a full disk; writing across a network mount that drops mid-write; the underlying ext4 flipped to read-only after detecting errors; cgroup/FS quota exhaustion in containers.

Common situations: Small VPS or container with a full volume; config dir on NFS/SMB with intermittent availability; container ephemeral storage limit reached; hardware disk failing and kernel remounts ro.

Related errors


AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16). Data as JSON: /api/errors/d3b0ffe354add716. Report an issue: GitHub.