eyaltoledano/claude-task-master · error · TaskMasterError

CONFIG_ERROR

CONFIG_ERROR

Error message

'Failed to save configuration'

What it means

ConfigPersistenceService.saveConfig writes config.json atomically (temp file + rename) and throws TaskMasterError with code CONFIG_ERROR when any step — mkdir, writeFile, or rename — fails. details.configPath identifies the target file; the original error is attached as cause.

Source

Thrown at packages/tm-core/src/modules/config/services/config-persistence.service.ts:72

			}

			// Ensure directory exists
			const configDir = path.dirname(this.localConfigPath);
			await fs.mkdir(configDir, { recursive: true });

			const jsonContent = JSON.stringify(config, null, 2);

			if (atomic) {
				// Atomic write: write to temp file then rename
				const tempPath = `${this.localConfigPath}.tmp`;
				await fs.writeFile(tempPath, jsonContent, 'utf-8');
				await fs.rename(tempPath, this.localConfigPath);
			} else {
				// Direct write
				await fs.writeFile(this.localConfigPath, jsonContent, 'utf-8');
			}
		} catch (error) {
			throw new TaskMasterError(
				'Failed to save configuration',
				ERROR_CODES.CONFIG_ERROR,
				{ configPath: this.localConfigPath },
				error as Error
			);
		}
	}

	/**
	 * Create a backup of the current configuration
	 */
	private async createBackup(): Promise<string> {
		try {
			await fs.mkdir(this.backupDir, { recursive: true });

			const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
			const backupPath = path.join(this.backupDir, `config-${timestamp}.json`);

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Check free disk space and file permissions on details.configPath and its directory, then retry
  2. Ensure the target is a regular writable file, not a directory or symlink loop
  3. Close tools that may lock the file (editors, antivirus, sync clients) or exclude the config dir from syncing
  4. Run the process as a user with write access to the config location, or point config at a writable path

Example fix

// before
await persistence.saveConfig(config); // throws on read-only dir
// after
try {
  await persistence.saveConfig(config);
} catch (e) {
  if (e instanceof TaskMasterError && e.code === 'CONFIG_ERROR') {
    await fs.chmod(path.dirname(e.details.configPath), 0o755); // fix perms
    await persistence.saveConfig(config); // retry
  } else { throw e; }
}
Defensive patterns

Strategy: retry

Validate before calling

import { access, constants } from 'fs/promises';
await access(path.dirname(configPath), constants.W_OK); // throws early if dir not writable
const st = await stat(configPath).catch(() => null);
if (st && !st.isFile()) throw new Error(`${configPath} is not a regular file`);

Type guard

function isConfigWriteError(e: unknown): e is TaskMasterError & { details: { configPath: string } } {
  return e instanceof TaskMasterError && e.code === 'CONFIG_ERROR'
    && typeof (e.details as any)?.configPath === 'string';
}

Try / catch

try {
  await persistence.saveConfig(config);
} catch (e) {
  if (isConfigWriteError(e)) {
    await fixPermissionsOrFreeDisk(e.details.configPath);
    await persistence.saveConfig(config); // retry once
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling saveConfig when the config directory doesn't exist and can't be created, the disk is full, permissions deny writing (EACCES/EPERM), the target is read-only, or the rename fails because the target is locked (EBUSY/EISDIR on Windows).

Common situations: Read-only filesystem or container without write access to home dir; disk quota exceeded; antivirus/backup tools locking the temp file during rename; running as a user without permission to ~/.task-master; config.json replaced by a directory.

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/e683d710406d5bcf. Report an issue: GitHub.