TencentCloud/TencentDB-Agent-Memory · error · SkillCoreError
SKILL_ID_COLLISION
SKILL_ID_COLLISION
Error message
failed to generate a unique skill_id after ${MAX_ID_ATTEMPTS} attempts What it means
During create, SkillCore generates a skill id (CSPRNG base62 ulid, ~71 bits) and preflights it against the store via getHeadIncludingArchived, retrying up to MAX_ID_ATTEMPTS (3). If all attempts collide with an existing id — including archived heads — it throws SKILL_ID_COLLISION. Since random collision probability is ~1e-10 per attempt, three consecutive hits means the id generator is broken, not unlucky.
Source
Thrown at MemoryCore/src/core/skill/skill-core.ts:285
// → 应用层 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}`;
// 全 team 范围查(不带 team_id):只要 skill_id 全局撞了就重试。
// 用 getHeadIncludingArchived 覆盖 archived 行——归档不代表 sid 空闲,
// 版本表 UNIQUE(skill_id, version) 仍会挡住写入。
const existing = await this.store.getHeadIncludingArchived(sid);
if (!existing) break;
if (attempt >= MAX_ID_ATTEMPTS) {
// 连续 3 次撞 —— 只可能是 ulid 注入器坏了(比如测试里固定返回同一值)
// 或熵源崩了,不是概率事件,直接抛。
throw new SkillCoreError(
"SKILL_ID_COLLISION",
`failed to generate a unique skill_id after ${MAX_ID_ATTEMPTS} attempts`,
);
}
}
try {
return await this.versioning.createNewSkill(
sid,
input.agent_id ?? "default",
{ user_id: input.user_id, team_id: input.team_id, agent_id: input.agent_id, task_id: input.task_id },
{
content: input.content,
name: input.name,
description: file.frontmatter.description,
resourcesToWrite: input.resources,
metadata_json: input.metadata ? JSON.stringify(input.metadata) : undefined,
},View on GitHub (pinned to 3efcd317b8)
Solutions
- Fix the ulid generator/injector so successive calls return fresh values (the code comments say 3 collisions means the injector is broken)
- In tests, make the fake generator return a sequence (e.g. incrementing values) instead of a fixed id
- Verify the entropy source (crypto.getRandomValues / crypto.randomBytes) is functioning in the runtime
- If collisions are genuinely from restored data, ensure archived skills share the same id namespace intentionally and adjust preflight or MAX_ID_ATTEMPTS
Example fix
// before: deterministic id in tests -> 3 collisions
const ids = ["AAAAAAAAAAAA"];
jest.spyOn(idgen, "generateId").mockImplementation(() => ids[0]);
// after: sequenced fake id
counter += 1;
jest.spyOn(idgen, "generateId").mockImplementation(() => `TESTID${String(counter).padStart(8, "0")}`); Defensive patterns
Strategy: retry
Validate before calling
// sanity-check the id generator before bulk creates
const samples = new Set(Array.from({ length: 100 }, () => generateId()));
if (samples.size < 100) throw new Error("ulid generator is producing duplicates"); Type guard
function idGeneratorHealthy(gen: () => string, n = 100): boolean {
const s = new Set<string>();
for (let i = 0; i < n; i++) s.add(gen());
return s.size === n;
} Try / catch
try {
return await core.create(input);
} catch (e) {
if (isSkillCoreError(e) && e.code === "SKILL_ID_COLLISION") {
// do NOT blind-retry: the generator is likely broken
logger.error("ulid injector appears deterministic/failing; aborting create");
throw new Error("skill id generator unhealthy", { cause: e });
}
throw e;
} Prevention
- Never stub the id generator with a constant in tests; use a sequence
- Health-check the entropy source at startup (generate and compare N ids)
- Do not retry SKILL_ID_COLLISION blindly — fix the generator first
- Keep the same id namespace assumptions across restore/archive tooling
When it happens
Trigger: The ulid injector/entropy source is deterministic or failed — e.g. tests fixing the generator to return the same value, a seeded/frozen CSPRNG, or Math.random/Date-based id generation that repeats — so getHeadIncludingArchived(sid) returns an existing head on every attempt.
Common situations: Unit tests with a stubbed id generator returning a constant; an id function closed over a stale timestamp; entropy source failure in constrained environments; replaying a captured id stream that re-issues the same id.
AI-assisted analysis of TencentCloud/TencentDB-Agent-Memory@3efcd317b8 (2026-09-01).
Data as JSON: /api/errors/e39a70a47d0610b7.
Report an issue: GitHub.