Skip to content

Skills client timeout is hardcoded at 30s and reports slow pulls as an unreachable server #6212

Description

@samuv

Bug description

The skills API client uses a fixed 30-second HTTP timeout with no CLI flag or environment override. Operations that legitimately exceed it — notably thv skill install pulling a large OCI artifact for the first time — fail with a message that blames server availability, when the server is healthy and still doing the work.

https://github.com/stacklok/toolhive/blob/main/pkg/skills/client/client.go#L29

defaultTimeout   = 30 * time.Second

WithTimeout exists as a library option but is not reachable from the CLI: newSkillClient calls skillclient.NewDefaultClient(ctx) with no options.

Steps to reproduce

TOOLHIVE_SKILLS_LOCK_ENABLED=true thv serve &
# any skill whose artifact takes >30s to pull on the current connection
thv skill install ghcr.io/stacklok/dockyard/skills/yara-rule-authoring:0.1.0 \
  --scope project --project-root "$PWD" --clients claude-code

Expected behavior

Either the timeout accommodates long-running pulls, or it is configurable, or the error distinguishes "the request timed out" from "the server is unreachable".

Actual behavior

Error: failed to install skill: could not reach ToolHive API server — is 'thv serve' running?: Post "http://127.0.0.1:8080/api/v1beta/skills": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Hint: ensure 'thv serve' is running

The server is running and unaffected; it logs the client hanging up mid-operation:

ERROR internal server error error="checking existing skill: scanning skill row: context canceled"

The hint sends users to check something that is not the problem. Retrying does not help, because the timeout is shorter than the operation regardless of warm caches.

Suggested fix

Some combination of:

  • treat context deadline exceeded separately from connection failures in formatSkillError, so the message says the operation timed out;
  • allow an override (a --timeout flag, or an env var read by NewDefaultClient);
  • consider a longer default for pull-backed operations, which are inherently network-bound and unbounded in size.

Environment

  • OS/version: macOS 15 (arm64)
  • ToolHive version: v0.42.0

Additional context

The same artifact pulls comfortably inside 30s on GitHub-hosted runners, so this reproduces based on connection speed and artifact size rather than deterministically.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcliChanges that impact CLI functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions