chenhg5/cc-connect · error

codex: write config.toml: %w

Error message

codex: write config.toml: %w

What it means

ensureCodexProviderConfig builds the updated provider section, upserts it into the existing config.toml content, and persists it with os.WriteFile(cfgPath, ..., 0o644). If the write fails, this error wraps the OS reason. The provider configuration was computed but could not be saved.

Source

Thrown at agent/codex/provider_config.go:35

		return nil
	}
	home, err := resolveCodexHomeForConfig(codexHome)
	if err != nil {
		return fmt.Errorf("codex: resolve codex home: %w", err)
	}
	if err := os.MkdirAll(home, 0o755); err != nil {
		return fmt.Errorf("codex: mkdir codex home: %w", err)
	}

	cfgPath := filepath.Join(home, "config.toml")
	raw, _ := os.ReadFile(cfgPath)
	content := string(raw)

	section := buildProviderSection(name, baseURL, wireAPI, headers)
	updated := upsertProviderSection(content, name, section)

	if err := os.WriteFile(cfgPath, []byte(updated), 0o644); err != nil {
		return fmt.Errorf("codex: write config.toml: %w", err)
	}
	slog.Debug("codex: wrote provider config", "provider", name, "path", cfgPath)
	return nil
}

// ensureCodexAuth writes $CODEX_HOME/auth.json with the provider's API key,
// matching cc-switch's approach: {"OPENAI_API_KEY": "...", "auth_mode": "api_key"}.
// This is the standard way to authenticate Codex CLI with third-party providers.
func ensureCodexAuth(codexHome, apiKey string) error {
	if apiKey == "" {
		return nil
	}
	home, err := resolveCodexHomeForConfig(codexHome)
	if err != nil {
		return fmt.Errorf("codex: resolve codex home: %w", err)
	}
	if err := os.MkdirAll(home, 0o755); err != nil {
		return fmt.Errorf("codex: mkdir codex home: %w", err)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped OS error; fix permissions on CODEX_HOME/config.toml (chown/chmod 0o644 writable by the process user).
  2. Ensure config.toml is a regular file, not a directory or read-only file.
  3. Check disk space/quota on the volume holding CODEX_HOME.
  4. Set CODEX_HOME to a writable location the cc-connect process user owns.

Example fix

// before
-rw------- root root ~/.codex/config.toml  # cc-connect runs as alice
// after
sudo chown alice:alice ~/.codex/config.toml && chmod 644 ~/.codex/config.toml
Defensive patterns

Strategy: validation

Validate before calling

cfgPath := filepath.Join(codexHome, "config.toml")
if fi, err := os.Stat(cfgPath); err == nil && (fi.IsDir() || fi.Mode().Perm()&0o200 == 0) {
    return fmt.Errorf("%s is not writable", cfgPath)
}
// also confirm the file is writable by opening for append
f, err := os.OpenFile(cfgPath, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil { return fmt.Errorf("config.toml not writable: %w", err) }
f.Close()

Try / catch

if err := ensureCodexProviderConfig(home, name, baseURL, wireAPI, headers); err != nil {
    if strings.Contains(err.Error(), "write config.toml") {
        return fmt.Errorf("check ownership/permissions of %s/config.toml: %w", home, err)
    }
    return err
}

Prevention

When it happens

Trigger: os.WriteFile on $CODEX_HOME/config.toml fails — permission denied on the file/dir, config.toml exists as a directory or read-only file, disk full, or the path was deleted between read and write — during StartSession provider setup.

Common situations: config.toml owned by root while cc-connect runs as another user; CODEX_HOME on a read-only volume; immutable/locked file (Windows AV lock); disk quota exceeded; tests using temp dirs that were cleaned up.

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 chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/f982f80780b2c709. Report an issue: GitHub.