abhigyanpatwari/GitNexus · error · Error

expected a YAML object

Error message

expected a YAML object

What it means

parseAutoSyncConfig() loads the auto-sync YAML file with the JSON schema and requires the top-level document to be a plain object (mapping). It throws 'expected a YAML object' when the parsed document is null, a scalar, or an array — the auto-sync config format only supports a mapping at the root.

Source

Thrown at gitnexus/src/core/auto-sync/config.ts:114

      message: `[auto-sync] Unable to read config file: ${configPath}. Auto sync is skipped.`,
    };
  }

  try {
    return { ok: true, config: parseAutoSyncConfig(content, configPath) };
  } catch (err: unknown) {
    return {
      ok: false,
      reason: 'invalid',
      message: `[auto-sync] Invalid watch_config.yml: ${(err as Error).message}. Auto sync is skipped.`,
    };
  }
}

export function parseAutoSyncConfig(content: string, configPath: string): AutoSyncConfig {
  const raw = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as Record<string, unknown>;
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
    throw new Error('expected a YAML object');
  }

  const errors: string[] = [];
  const interval = Number(raw.sync_interval_minutes);
  if (!Number.isInteger(interval) || interval <= 0) {
    errors.push('sync_interval_minutes must be a positive integer');
  } else if (interval < MIN_SYNC_INTERVAL_MINUTES) {
    errors.push(`sync_interval_minutes must be at least ${MIN_SYNC_INTERVAL_MINUTES}`);
  } else if (interval > MAX_SYNC_INTERVAL_MINUTES) {
    errors.push(`sync_interval_minutes must not exceed ${MAX_SYNC_INTERVAL_MINUTES}`);
  }

  // YAML booleans survive JSON_SCHEMA (`true`/`false`). `Number(true) === 1`
  // would otherwise pass the integer check and silently mean concurrency 1.
  let maxConcurrency = DEFAULT_MAX_CONCURRENCY;
  if (raw.max_concurrency !== undefined) {
    if (typeof raw.max_concurrency !== 'number' || !Number.isInteger(raw.max_concurrency)) {
      errors.push('max_concurrency must be a positive integer');

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Make the top level a mapping, e.g. sync_interval_minutes: 30 at the root.
  2. Remove surrounding list syntax ('- ' items) or wrap entries under a top-level key.
  3. If the file is empty, populate it with the required keys (sync_interval_minutes at minimum).
  4. Validate the YAML parses to an object: node -e "console.log(require('js-yaml').load(require('fs').readFileSync(path,'utf8')))".

Example fix

// before
- repo: git@github.com:org/repo.git
// after
sync_interval_minutes: 30
remotes:
  - git@github.com:org/repo.git
Defensive patterns

Strategy: validation

Validate before calling

const yaml = require('js-yaml');
const doc = yaml.load(fs.readFileSync(cfgPath, 'utf8'), { schema: yaml.JSON_SCHEMA });
if (!doc || typeof doc !== 'object' || Array.isArray(doc)) throw new Error('auto-sync config must be a YAML mapping');

Type guard

const isRecord = (v: unknown): v is Record<string, unknown> =>
  typeof v === 'object' && v !== null && !Array.isArray(v);

Prevention

When it happens

Trigger: A .gitnexus auto-sync config file whose content parses to a list (e.g. starts with '-'), an empty file (yaml.load returns null), or a bare scalar like just a number or quoted string.

Common situations: Accidentally pasting a YAML list of repos instead of a mapping, an empty config file created by touch, or YAML that's just a comment so it parses to null.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-09-08). Data as JSON: /api/errors/66236ca6b943f722. Report an issue: GitHub.