{"record":{"id":"a35fe9cdf985f5a8","repo":"TencentCloud/TencentDB-Agent-Memory","slug":"skill-patch-not-unique","errorCode":"SKILL_PATCH_NOT_UNIQUE","errorMessage":"old_string not found","messagePattern":"old_string not found","errorType":"error_code","errorClass":"SkillCoreError","httpStatus":null,"severity":"error","filePath":"MemoryCore/src/core/skill/skill-core.ts","lineNumber":343,"sourceCode":"      });\n      void this.versioning.cleanupExpiredVersionsForSkill(\n        head.skill_id, this.versionTtlSeconds,\n      ).catch(() => { /* fire-and-forget */ });\n      return result;\n    } catch (e) {\n      toCoreError(e);\n    }\n  }\n\n  async patch(input: PatchInput): Promise<Skill> {\n    const head = await this.requireHead(input.skill_id, input.team_id);\n    if (input.agent_id) assertOwnerWrap(head, input.agent_id, input.team_id);\n    assertVersionFreshWrap(head, input.expected_version);\n\n    // count occurrences\n    const occ = countOccurrences(head.content, input.old_string);\n    if (occ === 0) {\n      throw new SkillCoreError(\"SKILL_PATCH_NOT_UNIQUE\", `old_string not found`);\n    }\n    if (occ > 1 && !input.replace_all) {\n      throw new SkillCoreError(\"SKILL_PATCH_NOT_UNIQUE\", `old_string occurs ${occ} times; pass replace_all=true to replace all`);\n    }\n\n    const newContent = input.replace_all\n      ? splitJoin(head.content, input.old_string, input.new_string)\n      : head.content.replace(input.old_string, input.new_string);\n\n    // re-parse + validate\n    const file = this.parseAndValidate(newContent);\n    if (file.frontmatter.name !== head.name) {\n      throw new SkillCoreError(\"INVALID_FRONTMATTER\", \"patch attempted to rename skill\");\n    }\n\n    try {\n      const result = await this.versioning.appendNextVersion(head, this.ctxOf(input), {\n        content: newContent,","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/3efcd317b84146d6a08518ac0f7ee7c8a8d200ec/MemoryCore/src/core/skill/skill-core.ts#L325-L361","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()"],"exampleFix":"// before: patching a stale string\nawait core.patch({ skill_id, expected_version: 3, old_string: \"old step\", new_string: \"new step\" });\n// after: verify against fresh head first\nconst head = await core.get({ skill_id, team_id });\nif (!head.content.includes(old)) throw new Error(\"refresh old_string from head\");\nawait core.patch({ skill_id, expected_version: head.version, old_string: old, new_string: nu });","handlingStrategy":"retry","validationCode":"async function canPatch(core: SkillCore, skill_id: string, team_id: string, old: string): Promise<boolean> {\n  const head = await core.get({ skill_id, team_id });\n  return head.content.includes(old);\n}","typeGuard":"function patchIsApplicable(headContent: string, oldString: string): boolean {\n  return headContent.includes(oldString);\n}","tryCatchPattern":"try {\n  return await core.patch({ skill_id, expected_version, old_string, new_string });\n} catch (e) {\n  if (isSkillCoreError(e) && e.code === \"SKILL_PATCH_NOT_UNIQUE\" && e.message === \"old_string not found\") {\n    const head = await core.get({ skill_id, team_id }); // refresh, maybe content moved on\n    if (head.content.includes(old_string))\n      return core.patch({ skill_id, expected_version: head.version, old_string, new_string });\n    logger.warn(\"old_string genuinely absent from head; falling back to full update\");\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["patch","string-matching","stale-state","versioning"],"backgroundTag":"patch-target-not-found","analyzedSha":"3efcd317b84146d6a08518ac0f7ee7c8a8d200ec","analyzedAt":"2026-09-01T05:44:22.276Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}