TencentCloud/TencentDB-Agent-Memory · error · SkillCoreError
SKILL_PATCH_NOT_UNIQUE
SKILL_PATCH_NOT_UNIQUE
Error message
old_string not found
What it means
SkillCore.patch performs a string-substitution edit on the skill's current head content. It counts occurrences of old_string in head.content; if the count is zero it throws SkillCoreError SKILL_PATCH_NOT_UNIQUE with message "old_string not found", because there is nothing to replace.
Source
Thrown at MemoryCore/src/core/skill/skill-core.ts:343
});
void this.versioning.cleanupExpiredVersionsForSkill(
head.skill_id, this.versionTtlSeconds,
).catch(() => { /* fire-and-forget */ });
return result;
} catch (e) {
toCoreError(e);
}
}
async patch(input: PatchInput): Promise<Skill> {
const head = await this.requireHead(input.skill_id, input.team_id);
if (input.agent_id) assertOwnerWrap(head, input.agent_id, input.team_id);
assertVersionFreshWrap(head, input.expected_version);
// count occurrences
const occ = countOccurrences(head.content, input.old_string);
if (occ === 0) {
throw new SkillCoreError("SKILL_PATCH_NOT_UNIQUE", `old_string not found`);
}
if (occ > 1 && !input.replace_all) {
throw new SkillCoreError("SKILL_PATCH_NOT_UNIQUE", `old_string occurs ${occ} times; pass replace_all=true to replace all`);
}
const newContent = input.replace_all
? splitJoin(head.content, input.old_string, input.new_string)
: head.content.replace(input.old_string, input.new_string);
// re-parse + validate
const file = this.parseAndValidate(newContent);
if (file.frontmatter.name !== head.name) {
throw new SkillCoreError("INVALID_FRONTMATTER", "patch attempted to rename skill");
}
try {
const result = await this.versioning.appendNextVersion(head, this.ctxOf(input), {
content: newContent,View on GitHub (pinned to 3efcd317b8)
Solutions
- Re-read the head content (core.get) and verify old_string literally appears in it before patching
- Copy old_string exactly from the head content rather than retyping it; check for invisible whitespace and line-ending differences
- Re-run the patch after refreshing expected_version and old_string from the latest head
- If content drifted heavily, rewrite via update() with full content instead of patch()
Example fix
// before: patching a stale string
await core.patch({ skill_id, expected_version: 3, old_string: "old step", new_string: "new step" });
// after: verify against fresh head first
const head = await core.get({ skill_id, team_id });
if (!head.content.includes(old)) throw new Error("refresh old_string from head");
await core.patch({ skill_id, expected_version: head.version, old_string: old, new_string: nu }); Defensive patterns
Strategy: retry
Validate before calling
async function canPatch(core: SkillCore, skill_id: string, team_id: string, old: string): Promise<boolean> {
const head = await core.get({ skill_id, team_id });
return head.content.includes(old);
} Type guard
function patchIsApplicable(headContent: string, oldString: string): boolean {
return headContent.includes(oldString);
} Try / catch
try {
return await core.patch({ skill_id, expected_version, old_string, new_string });
} catch (e) {
if (isSkillCoreError(e) && e.code === "SKILL_PATCH_NOT_UNIQUE" && e.message === "old_string not found") {
const head = await core.get({ skill_id, team_id }); // refresh, maybe content moved on
if (head.content.includes(old_string))
return core.patch({ skill_id, expected_version: head.version, old_string, new_string });
logger.warn("old_string genuinely absent from head; falling back to full update");
}
throw e;
} Prevention
- Copy old_string verbatim from freshly read head content, never retype it
- Normalize line endings/whitespace before matching
- Refresh expected_version together with old_string from the same read
- Fall back to update() with full content when the head has diverged
When it happens
Trigger: Calling skillCore.patch({ skill_id, expected_version, old_string, new_string }) where old_string does not appear in the head version's content — e.g. the content was edited since you read it, whitespace/normalization differences, or you patched a different version than you inspected.
Common situations: Stale read: the skill was updated by someone else between your read and patch; Unicode/whitespace differences (smart quotes, trailing spaces, CRLF vs LF) making the string not literally match; assuming the patch applies to your draft rather than the head content.
Related errors
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/a35fe9cdf985f5a8.
Report an issue: GitHub.