fix: connector tools fail gracefully + operator allow/deny tool filter - #449
Open
Brad Huffman (ppsplus-bradh) wants to merge 4 commits into
Conversation
A `code --update --recursive` documentation run could crash uncaught when a
subagent invoked a connector-ingest tool without credentials configured (e.g.
`openwiki_ingest_connector({connectorId:"google"})` throwing "Gmail refresh
token is required for OAuth refresh"). The throw escaped the tool call and
propagated through the langgraph stream, terminating the whole run after it had
executed for a long time and produced no output.
Rather than hardcode-gating connector tools out of code mode (which would
over-remove `git-repo`, documented as relevant to code mode), this makes tool
calling resilient and gives operators explicit control:
- Resilience: every connector tool's func is wrapped so a thrown error becomes
a structured, model-visible result instead of an uncaught crash. Abort and
LangGraph graph-interrupt signals are re-thrown (never swallowed) so
cancellation and human-in-the-loop control flow keep working. ingestConnector
and ingestAllConnectors keep per-connector isolation with the same guard.
- Operator control: createOpenWikiConnectorTools accepts an allow/deny filter
(deny wins; empty allow = all; default = all, no behavior change), wired via
--allow-tools/--deny-tools CLI flags and OPENWIKI_ALLOW_TOOLS/
OPENWIKI_DENY_TOOLS env vars across both the run and scheduled `ingest`
(cron) paths.
- Prompt gating: the connector-ingestion discipline block is only emitted when
connector tools are actually exposed, so the agent is never told to call
tools it does not have.
Refs: langchain-ai#444 (also relates to langchain-ai#426, langchain-ai#427).
Co-Authored-By: Claude <noreply@anthropic.com>
Brad Huffman (ppsplus-bradh)
force-pushed
the
fix/connector-tool-resilience-and-operator-filter
branch
from
July 23, 2026 13:38
e453fba to
578dcb8
Compare
| } | ||
|
|
||
| // AbortController / cancellation. | ||
| if (error.name === "AbortError") { |
Collaborator
There was a problem hiding this comment.
nice - great catch here.
|
|
||
| // LangGraph GraphBubbleUp marker (GraphInterrupt, NodeInterrupt, GraphDrained, | ||
| // ParentCommand all set this getter to true). | ||
| if (error.is_bubble_up === true) { |
Collaborator
There was a problem hiding this comment.
nice catch here as well. thanks for handling these
Collaborator
|
In general a big focus for me is trying to prevent unnecessary crashes and making things degrade gracefully so thank you for getting started on this. |
# Conflicts: # src/cli.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #444.
What & why
A
code --update --recursivedocumentation run could crash uncaught when a subagent invoked a connector-ingest tool without credentials configured — e.g.openwiki_ingest_connector({connectorId:"google"})throwingGmail refresh token is required for OAuth refresh.The throw escaped the tool call and propagated through the langgraph stream, terminating the whole run after a long, expensive execution that produced zero output.Approach — a note on direction
The issue title suggested gating connector tools out of code mode. While implementing that, it became clear a blanket mode-gate over-removes
git-repo, which this repo's own docs (openwiki/integrations/connectors.md) describe as legitimately relevant to code mode ("documenting a different target repo than the one being ingested from"). Deciding code mode should have no connectors would contradict documented behavior.So this PR takes a more robust and less presumptuous route: make tool calling resilient, and let the operator decide which tools are exposed. This fixes the crash for all modes (not just code) and adds no behavior change by default.
Changes
Resilience — connector tools fail gracefully. Every connector tool's
funcis wrapped so a thrown error becomes a structured, model-visible result (Tool error: …/{status:"error"}) instead of an uncaught crash.ingestConnector/ingestAllConnectorskeep per-connector isolation (one failure doesn't abort the rest). Abort and LangGraph graph-interrupt signals are re-thrown, never swallowed, so cancellation and human-in-the-loop control flow keep working (isUncatchableErrormirrors langgraph'sis_bubble_upmarker +GraphInterrupt/NodeInterruptnames +AbortError).Operator control — allow/deny tool filter.
createOpenWikiConnectorTools(filter?)accepts{allow?, deny?}by tool name (deny wins; empty allow = all; default = all seven tools, no behavior change). Wired via--allow-tools/--deny-toolsCLI flags (space and=forms) andOPENWIKI_ALLOW_TOOLS/OPENWIKI_DENY_TOOLSenv vars (CLI wins over env) across both therunpath and the scheduledingest(cron) path — so operators can restrict tools in unattended CI runs, e.g.OPENWIKI_DENY_TOOLS=openwiki_ingest_connector,openwiki_ingest_all_connectors.Prompt gating by actual toolset. The "Connector ingestion discipline" block is only emitted when connector tools are actually present, so the agent is never instructed to call tools it doesn't have.
Relationship to #426 / #427
Complementary. #426 (unhandled-rejection crash) and #427 (connector tools should return errors, not throw) address the framework/tool-result layer generally; this PR adds the connector-specific graceful-failure wrapper (with abort/interrupt safety) plus operator-facing tool control and prompt consistency. The graceful-failure wrapper here overlaps #427's intent for connector tools specifically.
Testing
pnpm run typecheck,pnpm run lint:check,pnpm run format:check— all clean.pnpm run test— 375 tests pass (34 files).ingestAllConnectorspartial success, CLI/=-form flag parsing on bothrunandingest, and prompt block presence/absence by toolset.🤖 Generated with Claude Code