HKUDS/DeepTutor · error · HubError

: publish failed (HTTP )

Error message

{self.name}: publish failed (HTTP {response.status_code}): {detail}

What it means

Raised by ClawHubProvider.publish when the hub accepts the request but responds with HTTP >= 400. The message embeds the status code plus the hub's own error detail (JSON 'error' field, or the first 200 chars of the body).

Solutions

  1. Bump the version in SKILL.md frontmatter (or pass --version) and retry
  2. Re-mint the token on the hub web UI if 401/403
  3. Check ownership of the slug and rename if taken
  4. Fix any field validation errors named in the detail string

Example fix

# before: version: 1.0.0 already published
# after (SKILL.md frontmatter):
version: 1.0.1
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-check version novelty where possible
listing = hub.detail(slug)
if listing.get("version") == my_version: bump_version()

Try / catch

try:
    hub.publish(slug, version, token, ...)
except HubError as e:
    if "HTTP 409" in str(e): bump_and_retry()
    elif "HTTP 401" in str(e) or "HTTP 403" in str(e): relogin()
    else: raise

Prevention

When it happens

Trigger: Publishing with an invalid/expired bearer token (401), a slug owned by someone else (403), a version that already exists (409), or validation failures (422) on fields like slug/version.

Common situations: Re-publishing the same version instead of bumping it in SKILL.md frontmatter; token minted for a different account; missing required publish fields; slug name collision on the hub.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/fa49313a7653e55e. Report an issue: GitHub.

Appendix: source

Thrown at deeptutor/services/skill/hub.py:481

        accepts it. Surfaces the API's JSON ``error`` message on failure.
        """
        url = f"{self._base_url}/skills"
        try:
            response = self._client.post(
                url,
                data={"slug": slug, "version": version, **fields},
                files={"zip": ("package.zip", zip_bytes, "application/zip")},
                headers={"Authorization": f"Bearer {token}"},
            )
        except httpx.HTTPError as exc:
            raise HubError(f"{self.name}: publish request failed: {exc}") from exc
        if response.status_code >= 400:
            detail = ""
            try:
                detail = str(response.json().get("error") or "")
            except ValueError:
                detail = response.text[:200]
            raise HubError(f"{self.name}: publish failed (HTTP {response.status_code}): {detail}")
        try:
            return response.json()
        except ValueError:
            return {}

    def list_my_skills(self, token: str) -> list[dict[str, Any]]:
        """List the authenticated user's skills via ``GET /skills?owner=me``.

        Each row carries the slug, current ``version`` (latest), the full
        ``versions`` list (for rollback) and the current classification (for
        pre-filling an upgrade's tagging) — see ``buildMySkills`` on the hub.
        """
        url = f"{self._base_url}/skills"
        try:
            response = self._client.get(
                url, params={"owner": "me"}, headers={"Authorization": f"Bearer {token}"}
            )
        except httpx.HTTPError as exc:

View on GitHub (pinned to 3e82f13042)