yikart/AiToEarn · critical · AppException

ResponseCode.ConfigEditorWriteFailed

ResponseCode.ConfigEditorWriteFailed

Error message

ConfigEditorWriteFailed: error.message

What it means

writeConfigFile failed when saveConfig tried to persist the serialized config with fs writeFile (utf-8). The Node error is rethrown as AppException(ConfigEditorWriteFailed) with error.message, typically ENOSPC (disk full), EACCES (no write permission), EROFS (read-only filesystem), or ENOENT (parent directory missing).

Source

Thrown at project/aitoearn-backend/libs/config-editor/src/config-editor.service.ts:100

  private async readConfigFile(configPath: string) {
    try {
      return await readFile(configPath, 'utf-8')
    }
    catch (error) {
      throw new AppException(
        ResponseCode.ConfigEditorReadFailed,
        error instanceof Error ? error.message : String(error),
      )
    }
  }

  private async writeConfigFile(configPath: string, content: string) {
    try {
      await writeFile(configPath, content, 'utf-8')
    }
    catch (error) {
      throw new AppException(
        ResponseCode.ConfigEditorWriteFailed,
        error instanceof Error ? error.message : String(error),
      )
    }
  }
}

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Read error.message: ENOSPC → free disk space; EACCES → chown/chmod the file or run as a user with write access; EROFS/ENOENT → make the mount writable or recreate the directory
  2. Ensure the config file's volume is mounted rw, not :ro, in docker-compose/Kubernetes
  3. Give the container's runtime user ownership of the config directory (chown -R node:node /app/config)
  4. If the config is intentionally immutable (ConfigMap), stop exposing saveConfig / make the editor read-only instead of writing

Example fix

// before (docker-compose.yml)
volumes:
  - ./config/app.yaml:/app/config/app.yaml:ro
// after
volumes:
  - ./config/app.yaml:/app/config/app.yaml:rw
Defensive patterns

Strategy: try-catch

Validate before calling

import { accessSync, constants } from 'node:fs'
try { accessSync(resolvedPath, constants.W_OK) } catch { /* not writable — surface before editing */ }

Try / catch

try {
  await editor.saveConfig(newConfig)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.ConfigEditorWriteFailed) {
    if (e.message.includes('EROFS')) logger.error('Config filesystem is read-only')
    if (e.message.includes('ENOSPC')) logger.error('Disk full')
    if (e.message.includes('EACCES')) logger.error('No write permission')
  }
  throw e
}

Prevention

When it happens

Trigger: saveConfig() is called and the resolved configPath is in a read-only container filesystem or read-only volume mount, the process user lacks write permission, the disk is full, or the config file was deleted along with its directory after startup.

Common situations: Kubernetes/Docker mounts with the config baked as read-only (common for ConfigMap mounts); running as a non-root user in a container that owns no writable path; tmpfs full; disk quota exceeded; PM2 restart with a cwd whose config dir no longer exists.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/03a49e10637393c1. Report an issue: GitHub.