Yeachan-Heo/oh-my-codex · error · Error

${filePath} frontmatter "${key}" has an unterminated quoted

Error message

${filePath} frontmatter "${key}" has an unterminated quoted string

What it means

A frontmatter value starting with a quote must end with the same quote after at least 2 characters. This error fires for unterminated quoted strings, e.g. "my-skill (missing closing quote).

Source

Thrown at src/cli/setup.ts:2226

function parseSkillFrontmatterScalar(
	value: string,
	key: string,
	filePath: string,
): string {
	const trimmed = value.trim();
	if (!trimmed) {
		throw new Error(`${filePath} frontmatter "${key}" must not be empty`);
	}
	if (trimmed === "|" || trimmed === ">") {
		throw new Error(
			`${filePath} frontmatter "${key}" must be a single-line string`,
		);
	}

	const quote = trimmed[0];
	if (quote === '"' || quote === "'") {
		if (trimmed.length < 2 || trimmed.at(-1) !== quote) {
			throw new Error(
				`${filePath} frontmatter "${key}" has an unterminated quoted string`,
			);
		}
		const unquoted = trimmed.slice(1, -1).trim();
		if (!unquoted) {
			throw new Error(`${filePath} frontmatter "${key}" must not be empty`);
		}
		return unquoted;
	}

	const unquoted = trimmed.replace(/\s+#.*$/, "").trim();
	if (!unquoted) {
		throw new Error(`${filePath} frontmatter "${key}" must not be empty`);
	}
	return unquoted;
}

export function parseSkillFrontmatter(

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Close the quoted string: name: "my-skill"
  2. If the leading character is not meant as a quote (e.g. it's), rephrase or wrap the whole value in matched quotes
  3. Avoid smart/curly quotes; use plain ASCII quotes

Example fix

# before
name: "my-skill
# after
name: "my-skill"
Defensive patterns

Strategy: validation

Validate before calling

const line = 'name: "my-skill';
const v = line.replace(/^[^:]*:\s*/, "").trim();
const q = v[0];
const terminated = !(q === '"' || q === "'") || (v.length >= 2 && v.at(-1) === q);
if (!terminated) throw new Error("unterminated quoted frontmatter value");

Type guard

const isTerminatedQuoted = (v: string): boolean => {
  const q = v.trim()[0];
  if (q !== '"' && q !== "'") return true;
  const t = v.trim();
  return t.length >= 2 && t.at(-1) === q;
};

Try / catch

try { parseSkillFrontmatter(content); } catch (e) { if (e instanceof Error && e.message.includes("unterminated quoted string")) { /* add the matching closing quote on the flagged key */ } else throw e; }

Prevention

When it happens

Trigger: Values like 'name: "my-skill' or 'description: it's fine' where an opening double/single quote is not matched by a closing one at the end of the trimmed value.

Common situations: Apostrophes at the start of descriptions, smart quotes pasted from word processors, or simply forgetting the closing quote.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/b3d184570f03f875. Report an issue: GitHub.