agentscope-ai/agentscope · error · ValueError

A skill named {skill_record.name!r} already exists for this

Error message

A skill named {skill_record.name!r} already exists for this user.

What it means

Raised by SQLStorage.upsert_skill when a skill record's name is already owned by a different skill record for the same user. Skill names are unique per user; the guard compares the stored record's id to skill_record.id and rejects the write on mismatch.

Source

Thrown at src/agentscope/app/storage/_sql/_storage.py:914

                    MCPRow.id == mcp_id,
                    MCPRow.user_id == user_id,
                ),
            )
            await sess.commit()
        return result.rowcount > 0

    async def upsert_skill(
        self,
        user_id: str,
        skill_record: SkillRecord,
    ) -> str:
        """Create or update an installed-skill record for *user_id*.

        Same contract as :meth:`RedisStorage.upsert_skill`.
        """
        holder = await self.get_skill_by_name(user_id, skill_record.name)
        if holder is not None and holder.id != skill_record.id:
            raise ValueError(
                f"A skill named {skill_record.name!r} already exists for "
                f"this user.",
            )
        skill_record.user_id = user_id
        await self._write_row(SkillRow, skill_record)
        return skill_record.id

    async def list_skills(self, user_id: str) -> list[SkillRecord]:
        """Return every installed-skill record for *user_id*."""
        from sqlalchemy import select

        async with self._session() as sess:
            rows = (
                (
                    await sess.execute(
                        select(SkillRow).where(SkillRow.user_id == user_id),
                    )
                )

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Rename the incoming skill to something unique for that user
  2. Fetch the existing skill via get_skill_by_name and update it in place, preserving its id
  3. Remove the old conflicting skill before installing the new one

Example fix

// before
await storage.upsert_skill(user_id, SkillRecord(id=fresh, name="search"))
// after
old = await storage.get_skill_by_name(user_id, "search")
if old:
    await storage.delete_skill(user_id, old.id)
await storage.upsert_skill(user_id, SkillRecord(name="search", ...))
Defensive patterns

Strategy: validation

Validate before calling

if (dup := await storage.get_skill_by_name(user_id, rec.name)) and dup.id != rec.id:\n    raise Exception(f\"skill name {rec.name!r} taken\")

Try / catch

try:\n    await storage.upsert_skill(user_id, rec)\nexcept ValueError as e:\n    if \"already exists\" in str(e): rename_or_merge(rec)\n    else: raise

Prevention

When it happens

Trigger: Calling upsert_skill(user_id, record) where record.name collides with another skill of that user and record.id is not the existing record's id.

Common situations: Installing a second skill with the same display name; reinstalling a skill after its record was recreated with a fresh id; syncing skills between machines without carrying ids.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/24ac12b28b72d0b7. Report an issue: GitHub.