TencentCloud/TencentDB-Agent-Memory · error · SkillCoreError
INVALID_FRONTMATTER
INVALID_FRONTMATTER
Error message
frontmatter.name '${file.frontmatter.name}' != body.name '${input.name}' What it means
SkillCore.create parses the provided skill markdown and requires consistency between the YAML frontmatter "name" field and the "name" supplied in the request body. If they differ it throws SkillCoreError INVALID_FRONTMATTER, because the frontmatter name is the skill's identity and a mismatch would create ambiguous or drifting identities.
Source
Thrown at MemoryCore/src/core/skill/skill-core.ts:256
this.onSkillArchived = opts.onSkillArchived;
this.onSkillAccessed = opts.onSkillAccessed;
}
/** 读路径读到具体 skill 后 fire。异常吞掉,不阻塞读。 */
private notifyAccessed(skill: Skill): void {
if (!this.onSkillAccessed) return;
try { this.onSkillAccessed(skill); } catch { /* swallow */ }
}
// ───────────────────────────────────────────────────────────────────
// WRITE actions
// ───────────────────────────────────────────────────────────────────
async create(input: CreateInput): Promise<Skill> {
// 1) parse + validate
const file = this.parseAndValidate(input.content);
if (file.frontmatter.name !== input.name) {
throw new SkillCoreError("INVALID_FRONTMATTER", `frontmatter.name '${file.frontmatter.name}' != body.name '${input.name}'`);
}
// 2) 生成 sid 并做碰撞检测
//
// 背景:默认 ulid 走 CSPRNG base62 12 字符(~71 bit 真熵,单实例 100 万
// skill 时碰撞概率 ~1.5e-10),工程上"永远不会撞";但仍加一层 preflight
// 防御,把"撞了静默覆盖"变成"撞了 retry"。为什么不上 DB UNIQUE 约束:
// - SQLite skills 表 UNIQUE(skill_id, version) 已存在,v1 碰撞会被物理挡住
// - TCVDB 主键是 row_id(每行唯一),无法给 skill_id 加"仅 v1 唯一"约束
// (skill 天生多版本,version 2/3 就是同 skill_id 共存)
// → 应用层 preflight 是唯一可移植到两种 store 的方案。
//
// 注:注入的 ulid 工厂可能不带 'skl-' 前缀,这里兜底拼上。
const MAX_ID_ATTEMPTS = 3;
let sid = "";
for (let attempt = 1; attempt <= MAX_ID_ATTEMPTS; attempt++) {
const u = this.ulid();
sid = u.startsWith("skl-") ? u : `skl-${u}`;View on GitHub (pinned to 3efcd317b8)
Solutions
- Make frontmatter.name match input.name exactly (or vice versa) before calling create
- Derive one from the other programmatically instead of maintaining two literals
- Add a pre-submit check in your tooling that parses frontmatter and compares with the body name
Example fix
// before
await core.create({ name: "code-review", content: "---\nname: code-reviewer\n---\n..." });
// after
const name = "code-review";
await core.create({ name, content: `---\nname: ${name}\n---\n...` }); Defensive patterns
Strategy: validation
Validate before calling
import { parse as parseYaml } from "yaml";
function assertFrontmatterNameMatches(name: string, content: string): void {
const m = content.match(/^---\n([\s\S]*?)\n---/);
if (!m) throw new Error("skill content missing frontmatter");
const fm = parseYaml(m[1]) as { name?: string };
if (fm.name !== name)
throw new Error(`frontmatter.name '${fm.name}' must equal body.name '${name}'`);
}
assertFrontmatterNameMatches(input.name, input.content); // call before core.create Type guard
function frontmatterNameMatches(name: string, content: string): boolean {
const m = content.match(/^---\n([\s\S]*?)\n---/);
if (!m) return false;
return (parseYaml(m[1]) as { name?: string })?.name === name;
} Try / catch
try {
await core.create(input);
} catch (e) {
if (isSkillCoreError(e) && e.code === "INVALID_FRONTMATTER") {
// auto-heal: rewrite frontmatter name to match the body name
input.content = input.content.replace(/^(---\nname: ).*(\n---)/, `$1${input.name}$2`);
return core.create(input);
}
throw e;
} Prevention
- Generate skill markdown from a single template that interpolates the name in both places
- Never hand-copy frontmatter from another skill when creating
- Add a CI/pre-submit lint that compares frontmatter.name with the API body name
When it happens
Trigger: Calling skillCore.create({ name: "code-review", content: "---\nname: code-reviewer\n..." }) — frontmatter name != input.name. Typically the content was copied from another skill, renamed in one place only, or generated by a template with a placeholder name.
Common situations: Renaming a skill in the body but forgetting the frontmatter; copying an existing skill's markdown as a starting point; programmatic content generation where the name is interpolated into the body but not the frontmatter (or vice versa).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- memory 系统用户 key 必须匹配 sk-mem-[A-Za-z0-9_-]{32}
- llm.provider=proxy 且 useMemorySystemUserKey=false 时必须显式 llm.
- Generation log object key exceeds COS limit
- Invalid generation log key
- Invalid generation log cursor
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/bb4efde1c675b59b.
Report an issue: GitHub.