windmill-labs/windmill · error · MalformedLockfileError

wmill-lock.yaml is malformed (expected an object). Refusing

Error message

wmill-lock.yaml is malformed (expected an object). Refusing to operate to avoid corrupting the lockfile.

What it means

The wmill CLI stores dependency and sync state in wmill-lock.yaml. Before any operation it parses this file; if the parsed YAML is not a non-null object, the CLI throws MalformedLockfileError and refuses to proceed, because writing to a malformed lockfile would destroy user data. This guards against hand-edited or corrupted lockfiles.

Source

Thrown at cli/src/utils/metadata.ts:1252

    yamlStringify(inMemoryLock as Record<string, any>, yamlOptions),
    "utf-8",
  );
  inMemoryLock = null;
}

export async function readLockfile(): Promise<Lock> {
  if (inMemoryLock) return inMemoryLock;
  let parsed: unknown;
  try {
    parsed = await yamlParseFile(WMILL_LOCKFILE);
  } catch {
    const lock: Lock = { locks: {}, version: CURRENT_LOCK_VERSION };
    await writeFile(WMILL_LOCKFILE, yamlStringify(lock, yamlOptions), "utf-8");
    log.info(colors.green("wmill-lock.yaml created"));
    return lock;
  }
  if (typeof parsed != "object" || parsed == null) {
    throw new MalformedLockfileError(
      "wmill-lock.yaml is malformed (expected an object). " +
      "Refusing to operate to avoid corrupting the lockfile.",
    );
  }
  const conf = parsed as Lock;
  if (conf.version != null && !KNOWN_LOCK_VERSIONS.includes(conf.version)) {
    throw new UnknownLockVersionError(
      `wmill-lock.yaml is at unknown version "${conf.version}". This was ` +
      `written by a newer wmill CLI; please upgrade with \`wmill upgrade\`. ` +
      `Refusing to operate to avoid corrupting the lockfile.`,
    );
  }
  return conf;
}

function v2LockPath(path: string, subpath?: string) {
  const normalizedPath = normalizeLockPath(path);
  if (subpath) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Open wmill-lock.yaml and ensure the top level is a YAML mapping with keys like 'locks' and 'version'
  2. If the file is corrupted or unimportant, delete it and regenerate it with the wmill CLI (e.g. re-run the sync/pull command that recreates the lockfile)
  3. Restore a previous version from git (git checkout -- wmill-lock.yaml) or from a backup
  4. Fix YAML syntax errors such as stray characters, tabs, or unquoted values that cause the document to parse as a scalar

Example fix

# before (malformed)
wmill-lock.yaml:
- just a list

# after (valid)
version: 1
locks: {}
Defensive patterns

Strategy: validation

Validate before calling

import { parse } from "yaml";
const parsed = parse(await readFile("wmill-lock.yaml", "utf-8"));
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
  throw new Error("wmill-lock.yaml is not an object — restore it from git or delete and regenerate it before running wmill.");
}

Type guard

function isLockObject(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await wmillSyncPull();
} catch (e) {
  if (e instanceof MalformedLockfileError) {
    // restore from git or delete + regenerate the lockfile
  } else throw e;
}

Prevention

When it happens

Trigger: Running any wmill command that reads the lockfile (sync, push, pull, deploy, etc.) when wmill-lock.yaml parses to a scalar, array, string, number, or is empty/null instead of a mapping object.

Common situations: Manually editing wmill-lock.yaml and accidentally replacing the top-level mapping with a scalar or list; truncating the file during a failed write or git merge conflict; a tool rewriting the file with the wrong root type; checking in an empty or placeholder lockfile.

Understand the failure class

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/4361ff16ffe2fdb2. Report an issue: GitHub.