OpenHands / OpenHands/software-agent-sdk
[Bug]: `parse_extension_source` duplicates an existing `.git` suffix in GitHub shorthand
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Is there an existing issue for the same bug?
- I have searched existing issues and this is not a duplicate.
Bug Description
In openhands-sdk/openhands/sdk/extensions/fetch.py, parse_extension_source unconditionally appends .git when expanding the github:owner/repository shorthand.
A shorthand whose repository component already has the conventional Git suffix, such as github:owner/repo.git, passes the current format validation but is expanded to:
https://github.com/owner/repo.git.git
The generated URL is not clone-equivalent to the intended repository URL. Extension fetching subsequently attempts to clone the duplicated-suffix URL instead of either normalizing it or rejecting the shorthand during parsing.
Expected Behavior
parse_extension_source should not accept a GitHub shorthand and return a duplicated .git.git clone URL.
A consistent normalization for:
parse_extension_source("github:owner/repo.git")
would be:
(SourceType.GITHUB, "https://github.com/owner/repo.git")
If an already-suffixed shorthand is intentionally unsupported, it should instead be rejected immediately with ExtensionFetchError. It should not be accepted and converted into an unusable clone URL.
Actual Behavior
parse_extension_source returns:
(SourceType.GITHUB, "https://github.com/owner/repo.git.git")
The input passes the shorthand validation because its repository component contains exactly one /. The duplicated suffix is introduced only when the normalized URL is constructed.
For a public repository, GitHub's smart-HTTP endpoint returned 200 for the canonical .git URL and 401 for the generated .git.git URL. A subsequent Git clone therefore fails instead of accessing the intended public repository.
Steps to Reproduce
-
Check out Software Agent SDK
mainat commit98338ff37aea6627777b9978963ab727f51e4f40. -
Set up the source checkout using the documented development command:
make build
- Add the following test to
tests/sdk/extensions/test_fetch.py:
def test_parse_github_shorthand_with_existing_git_suffix():
source_type, url = parse_extension_source("github:owner/repo.git")
assert source_type == SourceType.GITHUB
assert url == "https://github.com/owner/repo.git"
- Run:
uv run pytest tests/sdk/extensions/test_fetch.py -q
- Observe that the newly added assertion fails while the 41 existing tests pass.
Acceptance Criteria
-
parse_extension_source("github:owner/repo.git")does not return a URL ending in.git.git. - An already-suffixed GitHub shorthand is either normalized to one
.gitsuffix or rejected during parsing with a clearExtensionFetchError. - The existing
github:owner/reposhorthand continues to normalize tohttps://github.com/owner/repo.git. - Full HTTPS and SSH Git URLs ending in
.gitremain unchanged. - Existing invalid-format checks for missing or excessive path components remain unchanged.
- Regression coverage includes a GitHub shorthand whose repository component already ends in
.git.
Installation Method
Source checkout; development dependencies installed with make build; focused pytest reproduction
If you selected "Other", please specify
No response
SDK Version
main@98338ff37aea6627777b9978963ab727f51e4f40 (openhands-sdk 1.42.1); also reproduced at 007721b3d2bfccd1469f8008a514af13b7ae1b71
Version Confirmation
- I have confirmed this bug exists on the LATEST version of OpenHands SDK
Python Version
3.13.2
Model Name (if applicable)
Not applicable; reproduced by directly invoking deterministic extension-source parsing code
Operating System
MacOS
Logs and Error Messages
tests/sdk/extensions/test_fetch.py ..F.................................. [ 88%]
..... [100%]
FAILED tests/sdk/extensions/test_fetch.py::test_parse_github_shorthand_with_existing_git_suffix
AssertionError: assert 'https://gith.../repo.git.git' == 'https://gith...wner/repo.git'
- https://github.com/owner/repo.git
+ https://github.com/owner/repo.git.git
? ++++
1 failed, 41 passed in 0.16s
Minimal Code Sample
from openhands.sdk.extensions.fetch import parse_extension_source
result = parse_extension_source("github:owner/repo.git")
print(result)
# (<SourceType.GITHUB: 'github'>,
# 'https://github.com/owner/repo.git.git')
Screenshots and Additional Context
At the tested commit, the GitHub-shorthand branch validates only the number of path separators and then appends .git unconditionally:
if source.startswith("github:"):
repo_path = source[7:]
if "/" not in repo_path or repo_path.count("/") > 1:
raise ExtensionFetchError(
f"Invalid GitHub shorthand format: {source}. "
f"Expected format: github:owner/repo"
)
url = f"https://github.com/{repo_path}.git"
return (SourceType.GITHUB, url)
Consequently, github:owner/repo.git is treated as valid input and becomes https://github.com/owner/repo.git.git.
The same module already uses normalize_git_url for full Git URLs. That helper adds .git only when the URL does not already end with it:
if url.startswith(("https://", "http://")) and not url.endswith(".git"):
url = url.rstrip("/")
url = f"{url}.git"
One possible fix direction is to construct the GitHub HTTPS URL without manually adding a suffix and pass it through the existing idempotent normalizer:
url = normalize_git_url(f"https://github.com/{repo_path}")
Alternatively, the parser can explicitly reject an already-suffixed shorthand. In either case it should not return .git.git.
parse_extension_source is called by the extension-fetching path before the normalized URL is passed to the repository clone helper, so the malformed URL propagates beyond the parser.
Environment:
- OpenHands SDK:
1.42.1 - Tested snapshot:
007721b3d2bfccd1469f8008a514af13b7ae1b71 - Current main:
98338ff37aea6627777b9978963ab727f51e4f40 - Python:
3.13.2 - pytest:
9.0.3 - Operating system: macOS
15.7.3, arm64 - Installation: source checkout with the documented development setup
- Model/provider: not applicable
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in openhands-sdk/openhands/sdk/extensions/fetch.py at parse_extension_source and review the GitHub shorthand branch alongside normalize_git_url. Run uv run pytest tests/sdk/extensions/test_fetch.py -q, then use the reported regression case to verify that suffixed and unsuffixed shorthand inputs meet the acceptance criteria without changing full Git URL handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100