can1357/oh-my-pi · error

Managed skill "${name}" SKILL.md is not a regular file; refu

Error message

Managed skill "${name}" SKILL.md is not a regular file; refusing to overwrite it.

What it means

When overwriting an existing managed SKILL.md, the stat of the target must be a regular file. assertManagedSkillFileSafeForUpdate throws if it is a directory, FIFO, device, or other non-regular file, refusing to clobber something that isn't a plain file.

Source

Thrown at packages/coding-agent/src/autolearn/managed-skills.ts:131

 * follows intermediate components, so a symlinked root would let an otherwise
 * valid name write/delete outside the isolated directory (e.g. onto authored
 * skills). Checked before composing any child path.
 */
async function assertManagedRootSafe(): Promise<void> {
	const rootStat = await fs.lstat(getManagedSkillsDir()).catch(err => {
		if (isEnoent(err)) return null;
		throw err;
	});
	if (rootStat?.isSymbolicLink()) {
		throw new Error("The managed-skills root is a symlink; refusing to operate outside the managed directory.");
	}
}

const UPDATE_FILE_OPEN_FLAGS = fsConstants.O_WRONLY | fsConstants.O_NOFOLLOW;

function assertManagedSkillFileSafeForUpdate(name: string, fileStat: Stats): void {
	if (!fileStat.isFile()) {
		throw new Error(`Managed skill "${name}" SKILL.md is not a regular file; refusing to overwrite it.`);
	}
	if (fileStat.nlink > 1) {
		throw new Error(
			`Managed skill "${name}" SKILL.md has ${fileStat.nlink} hard links; refusing to overwrite a file that may be user-authored elsewhere.`,
		);
	}
}

async function openManagedSkillFileForUpdate(name: string, file: string) {
	try {
		return await fs.open(file, UPDATE_FILE_OPEN_FLAGS);
	} catch (err) {
		if ((err as { code?: string }).code === "ELOOP") {
			throw new Error(`Managed skill "${name}" SKILL.md is a symlink; refusing to overwrite it.`);
		}
		throw err;
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the path with `ls -la ~/.omp/agent/managed-skills/<name>/SKILL.md` to see what it actually is
  2. Remove or rename the offending non-regular entry, then retry the write
  3. If it should be a normal skill, recreate it: delete the bad entry and let writeManagedSkill create a fresh SKILL.md

Example fix

// before (shell): SKILL.md is a directory
mv ~/.omp/agent/managed-skills/foo/SKILL.md ~/.omp/agent/managed-skills/foo/SKILL.md.bak-dir
// after (shell): writeManagedSkill then recreates a regular file
writeManagedSkill({ name: "foo", action: "update", ... })
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from "node:fs/promises";
const st = await stat(skillMdPath).catch(() => null);
if (st && !st.isFile()) throw new Error("SKILL.md is not a regular file");

Type guard

function isRegularFile(st: { isFile(): boolean }): boolean {
  return st.isFile();
}

Try / catch

try {
  await writeManagedSkill({ name, action: "update", ... });
} catch (err) {
  if (String((err as Error).message).includes("not a regular file")) {
    // inspect/remove the bad entry at managed-skills/<name>/SKILL.md, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: writeManagedSkill (update path) stats an existing <name>/SKILL.md whose stat reports a non-regular file type — e.g. SKILL.md was replaced by a directory or a special file.

Common situations: Someone manually created a SKILL.md directory inside the managed skill folder; a broken tooling sync left a FIFO or socket at that path; a partial filesystem state after a crash.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/af8f11737b0b49a3. Report an issue: GitHub.