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

  1. Re-read the head content (core.get) and verify old_string literally appears in it before patching
  2. Copy old_string exactly from the head content rather than retyping it; check for invisible whitespace and line-ending differences
  3. Re-run the patch after refreshing expected_version and old_string from the latest head
  4. 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

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.