fix(extensions): compose local source with repo_path - #4839
Open
VascoSch92 wants to merge 3 commits into
Open
Conversation
A local source plus repo_path was rejected outright, so a Canvas extension in a monorepo could only be installed by passing its full directory as the source. The subpath join already existed for git sources; local sources now use it too, with a containment check so a traversing repo_path cannot escape the source directory. The install endpoint also replaced every fetch failure with one generic "check that the source is valid" message, which misattributed a bad repo_path to the source. It now passes the specific reason through (credential-redacted) and names the manifest when none is found at the resolved location.
Contributor
Python API breakage checks — ✅ PASSEDResult: ✅ PASSED |
Contributor
REST API breakage checks (OpenAPI) — ✅ PASSEDResult: ✅ PASSED |
Contributor
5 tasks
The canvas client classifies any message containing 'failed to fetch' as a network outage, so the install failure reason was replaced by a 'Disconnected, check your network' toast. Word it 'Could not read' and assert the phrase stays out.
VascoSch92
marked this pull request as ready for review
September 3, 2026 06:59
3 tasks
Collaborator
|
🚦 CI is currently failing on this PR's latest commit. Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request This is an automated check - no AI was used to generate this comment. |
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.
HUMAN:
BE needed to install extensions from local path
AGENT:
Why
Installing a canvas extension by pointing Source at a repository and Path at the
extension's subdirectory always fails.
fetch_with_resolution()rejectsrepo_pathoutright forSourceType.LOCALbefore the subpath is ever applied,so the composition that already works for git and GitHub sources is unreachable
for a local checkout.
The failure is also unreadable. The install route catches
ExtensionFetchErrorand replaces it with one fixed sentence about the source, so a typo in Path is
reported as a bad Source. On
origin/mainsix of seven distinct mistakes returnthe exact same message.
Reported as OSS-10405 and as OpenHands/OpenHands#17048.
Summary
fetch.py: local sources now compose withrepo_paththe same way git sourcesdo, via
_apply_subpath(), instead of raising._apply_subpath()gained containment. A subpath that climbs out of the basethrough
..or a symlink is rejected rather than resolved.canvas_extensions_router.py: the install route forwards the specific fetchreason (credential-redacted) and answers a missing manifest with its own 422
telling the user that Source plus Path must name the extension's own directory.
treats any message containing "failed to fetch" as a network outage and
replaces it with a "Disconnected, check your network" toast, which hid the
reason. A test asserts the phrase stays out.
Issue Number
OpenHands/OpenHands#17048, Linear OSS-10405.
How to Test
The ticket's own reproduction, run end to end against the real installer
(
install_canvas_extension), with a fixture repository holdingcanvas-extensions/canvas-pulse/canvas-extension.json:On
origin/main(e266832):On this branch:
Installed content was checked, not just the return value: only the named
subdirectory is copied (a sibling
other-extension/in the same repository isnot),
repo_pathis recorded on the installed record, and the extension staysenabled=Falseas before.Second, what the HTTP API says for each realistic mistake, driven through
TestClientagainstcanvas_extensions_router.On
origin/main:On this branch:
The 409 on the second row is the store rejecting a duplicate name, because the
first row already installed
canvas-pulseinto the shared fixture store. Slashtolerance is covered on its own by the parametrized router test.
Test suites:
Each new regression test was confirmed to fail with its fix reverted.
Browser verification
Ran the Canvas dev stack against this branch's agent-server:
with a fixture at
/tmp/repository-name/demo-extensionplus a siblingother-extension. Installing through the UI with Source/tmp/repository-nameand Path
demo-extensionsucceeded, and the resulting record proves thecomposition rather than a full-path install:
The agent-server log for the failure cases, taken from the same session:
This browser run is what surfaced the "failed to fetch" wording collision: the
server logged the correct 400 while the UI showed a network-outage toast. The
unit test did not catch it because it used a stand-in for the client's error
class.
Video/Screenshots
Terminal output above is the reproduction evidence: the four failing ticket cases
on
origin/main, the same four passing here, and the before/after API messages.Type
Notes
wording the improved 400 was still displayed as a network outage, because
isCorsOrNetworkErrorMessage()in the canvas frontend matches the substring"failed to fetch". Reworded here so every consumer benefits, not just the one
hook the companion PR touches.
fetch.pyis shared by Plugins, Skills and Canvas Extensions, so Plugins andSkills gain the same local Source + Path composition. Two plugin tests that
asserted the old rejection were rewritten to the new contract.
_apply_subpath()also tightens git and GitHubsources, which previously accepted a traversing
repo_path.fix(canvas-extensions): show the install failure reason OpenHands#17120. It shows the improved text only once this lands.
🐳 Agent Server images for this PR — GHCR package, pull/run commands, and all pushed tags (click to expand)
• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server
Variants & Base Images
eclipse-temurin:17-jdknikolaik/python-nodejs:python3.13-nodejs22-slimnikolaik/python-nodejs:python3.13-nodejs22-slimgolang:1.21-bookwormPull (multi-arch manifest)
# Each variant is a multi-arch manifest supporting both amd64 and arm64 docker pull ghcr.io/openhands/agent-server:423fbe6-pythonRun
All tags pushed for this build
About Multi-Architecture Support
423fbe6-python) is a multi-arch manifest supporting both amd64 and arm64423fbe6-python-amd64) are also available if needed