yikart/AiToEarn · error · AppException

ResponseCode.ConfigEditorUnsupportedFormat

ResponseCode.ConfigEditorUnsupportedFormat

Error message

ConfigEditorUnsupportedFormat

What it means

ConfigEditorService infers the config file format purely from the file extension of the configured configPath: only .json, .yaml and .yml are supported. If the extension matches neither, getConfigFileFormat throws this AppException before any file I/O happens. It is a configuration-time misuse of the config-editor library, not a runtime data problem.

Solutions

  1. Rename the config file (or change configPath) so it ends with .json, .yaml or .yml
  2. If the underlying format is not JSON/YAML, convert the file content to JSON or YAML and update configPath
  3. If the extension is right but case or casing suffix is wrong, note the check is case-insensitive, so 'CONFIG.JSON' works — fix spelling instead

Example fix

// before
ConfigEditorModule.register({ configPath: 'config/app.ini', schema: AppSchema })
// after
ConfigEditorModule.register({ configPath: 'config/app.yaml', schema: AppSchema })
Defensive patterns

Strategy: validation

Validate before calling

const p = configPath.toLowerCase()
if (!p.endsWith('.json') && !p.endsWith('.yaml') && !p.endsWith('.yml'))
  throw new Error(`configPath must be .json/.yaml/.yml, got: ${configPath}`)

Type guard

function isSupportedConfigPath(path: string): boolean {
  return /\.(json|ya?ml)$/i.test(path)
}

Try / catch

try {
  await editor.getConfig()
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.ConfigEditorUnsupportedFormat)
    logger.error(`Unsupported config extension: ${cfg.configPath}`)
  throw e
}

Prevention

When it happens

Trigger: ConfigEditorService.getConfig(), getConfig() indirectly, or saveConfig() is called while this.config.configPath (resolved against process.cwd()) does not end in .json, .yaml or .yml — e.g. 'config.ini', 'settings', 'app.conf', or '.toml'.

Common situations: Pointing the config editor at a TOML/INI/.env/properties file; passing an extensionless path; a typo like 'config.JSONX' or 'config.yml.bak'; mounting the config under a path with a query or suffix appended by a deploy tool.

Related errors


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

Appendix: source

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

    this.validateConfigValue(config)
  }

  async saveConfig(config: Record<string, unknown>) {
    const configPath = resolve(process.cwd(), this.config.configPath)
    const format = this.getConfigFileFormat(configPath)
    const content = this.serializeConfig(this.validateConfigValue(config), format)
    await this.writeConfigFile(configPath, content)
  }

  private getConfigFileFormat(filePath: string): ConfigFileFormat {
    const lowerPath = filePath.toLowerCase()
    if (lowerPath.endsWith('.json')) {
      return ConfigFileFormat.Json
    }
    if (lowerPath.endsWith('.yaml') || lowerPath.endsWith('.yml')) {
      return ConfigFileFormat.Yaml
    }
    throw new AppException(ResponseCode.ConfigEditorUnsupportedFormat)
  }

  private parseConfig(content: string, format: ConfigFileFormat) {
    try {
      return format === ConfigFileFormat.Json
        ? JSON.parse(content)
        : parseYaml(content)
    }
    catch (error) {
      throw new AppException(
        ResponseCode.ConfigEditorParseFailed,
        error instanceof Error ? error.message : String(error),
      )
    }
  }

  private validateConfigValue(config: unknown): Record<string, unknown> {
    const schema = isZodDto(this.config.schema) ? this.config.schema.schema : this.config.schema

View on GitHub (pinned to d3aa8bea5b)