Skip to content

A skill that fails to load is dropped silently #254

Description

@brandomr

Summary

When SkillIntegrationProvider cannot load a skill, it logs at debug level and continues with that skill simply absent. Nothing reaches the user, and nothing reaches the agent. For a remote skill — where a network blip, a moved file, or a renamed branch is routine — this means the agent quietly loses a capability it is supposed to have, and behaves as though the library it was taught about does not exist.

Reproduction

A skills.json pointing at an unreachable host:

["https://raw.githubusercontent.invalid/accord-research/rosetta/main/skills/rosetta/"]
provider = SkillIntegrationProvider(skill_paths=["skills.json"])
print(len(provider._skills))   # 0
print(repr(provider.prompt))   # ''

No exception, no warning, and it returns in 0.0s. The agent's prompt is empty — indistinguishable from a context that never declared any skills.

Why it matters

The failure is worst exactly where it is most likely. A local skill that goes missing usually means someone edited the repo and will notice. A remote skill fails for reasons entirely outside the user's session: GitHub being briefly unavailable, a skill moved to a new path, a main rename, a corporate proxy. In all of those the user sees an agent that has silently gotten worse at its job, with no thread to pull on.

It is also hard to diagnose after the fact. The natural conclusion when an agent stops using a library is that the prompt or the model regressed; the actual cause is a fetch that failed hours ago and logged at a level nobody has enabled.

Origin

discover_integrations swallows everything per-source:

def _load_deduped(source: str, base_path: Optional[str]=None):
    try:
        skill = cls._load_skill(source, base_path=base_path)
        ...
    except Exception:
        logger.exception("Failed to load skill from source: %s", source)

Broadening the except is not the fix — one bad entry should not take down the others, and that part is right. The gap is that a partial failure never becomes visible state.

Suggested direction

Roughly in order of value:

  1. Raise the log level. logger.exception here is at effective debug visibility in normal runs; a failed skill load is at least a warning.
  2. Record failures on the provider — something like self._failed: list[tuple[str, str]] of (source, error) — so the information survives past the call.
  3. Surface them in the Integrations panel, alongside the skills that did load. A user looking for "why doesn't it know about X" would find it in the one place they would think to look.
  4. Optionally, mention them in the provider prompt, so the agent can say "I was supposed to have a rosetta skill but could not load it" rather than confabulating from memory of the library.

Happy to put up a PR for 1–3 if the direction seems right.

Context

Found while wiring a context that loads two skills remotely from their source repositories (related: #253, which fixes remote skills exposing no examples).

🤖 Generated with Claude Code

https://claude.ai/code/session_01555XSwMPRxBZFAEsvubUMz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions