-
Notifications
You must be signed in to change notification settings - Fork 675
Description
Bug Report: URL Construction Bug in python-gitlab
Description of the problem, including code/CLI snippet
The _build_url
method in gitlab/client.py
incorrectly constructs URLs when the path parameter starts with /api/v4/
. This results in double /api/v4
segments in the final URL, causing 404 errors for API calls.
Code snippet demonstrating the bug:
import gitlab
# Create GitLab instance
gl = gitlab.Gitlab("https://api.example.com", private_token="fake_token", api_version="4")
# This call fails due to URL construction bug
path = "/api/v4/projects/123/security_settings"
result = gl._build_url(path)
print(result)
# Expected: https://api.example.com/api/v4/projects/123/security_settings
# Actual: https://api.example.com/api/v4/api/v4/projects/123/security_settings
# ^^^^^^^^ Double /api/v4
Expected Behavior
When calling _build_url
with a path starting with /api/v4/
, the method should return a correctly constructed URL without duplicate /api/v4
segments.
Expected output:
https://api.example.com/api/v4/projects/123/security_settings
Actual Behavior
The method returns a malformed URL with duplicate /api/v4
segments, causing API calls to fail with 404 errors.
Actual output:
https://api.example.com/api/v4/api/v4/projects/123/security_settings
Specifications
- python-gitlab version: ALL VERSIONS (5.0.0, 5.6.0, 6.0.0, 6.1.0, 6.2.0)
- Gitlab server version: Any version (this is a client-side bug)
- Python version: 3.9+ (all supported versions)
- Operating System: All platforms
Root Cause Analysis
The issue is in the _build_url
method in gitlab/client.py
:
def _build_url(self, path: str) -> str:
if path.startswith("http://") or path.startswith("https://"):
return path
return f"{self._url}{path}" # ← BUG HERE!
Problem: When path
starts with /api/v4/...
, it gets appended to self._url
which is already https://api.example.com/api/v4
, resulting in double /api/v4
segments.
Impact Assessment
High Impact
- API calls fail with 404 errors when using relative paths starting with
/api/v4/
- Direct HTTP requests work (using full URLs), but python-gitlab library methods fail
- Affects real-world usage where developers expect to use relative paths
- This bug has existed for years and affects all versions of python-gitlab
Real-World Example
Our script was trying to call:
gl.http_get(f"/api/v4/projects/{project.id}/security_settings")
But this resulted in:
https://gitlab.example.com/api/v4/api/v4/projects/369347/security_settings
Which caused a 404 error, making it appear that the endpoint didn't exist.
Test Case
A test case has been added to tests/unit/test_gitlab_http_methods.py
:
def test_build_url_with_api_v4_prefix_bug():
"""Test that reproduces the double /api/v4 bug in URL construction."""
gl = gitlab.Gitlab(
"https://api.example.com",
private_token="fake_token",
api_version="4"
)
problematic_path = "/api/v4/projects/369347/security_settings"
built_url = gl._build_url(problematic_path)
expected_url = "https://api.example.com/api/v4/projects/369347/security_settings"
assert built_url == expected_url, f"URL construction bug: got {built_url}, expected {expected_url}"
Test Results:
- All versions fail this test consistently
- This is NOT a regression - the bug has existed for years
Version Testing Results
Version 6.2.0 (Current)
- Status: ❌ FAILS
- Result:
https://api.example.com/api/v4/api/v4/projects/369347/security_settings
Version 6.1.0
- Status: ❌ FAILS
- Result: Same failure as 6.2.0
Version 6.0.0
- Status: ❌ FAILS
- Result: Same failure as 6.2.0
Version 5.6.0
- Status: ❌ FAILS
- Result: Same failure as 6.2.0
Version 5.0.0
- Status: ❌ FAILS
- Result: Same failure as 6.2.0
Proposed Solution
I have implemented a fix for this bug. The _build_url
method should detect and handle paths that start with /api/v4/
to avoid duplication:
def _build_url(self, path: str) -> str:
if path.startswith("http://") or path.startswith("https://"):
return path
# Fix: Remove /api/v4 prefix if it matches the instance's API version
if path.startswith(f"/api/v{self._api_version}/"):
# Remove the /api/v4 prefix to avoid duplication
path = path[len(f"/api/v{self._api_version}/"):]
return f"{self._base_url}/api/v{self._api_version}/{path}"
return f"{self._url}{path}"
Workarounds
1. Use Full URLs
# Instead of:
gl.http_get("/api/v4/projects/123/security_settings")
# Use:
gl.http_get("https://api.example.com/api/v4/projects/123/security_settings")
2. Construct Paths Without /api/v4 Prefix
# Instead of:
gl.http_get("/api/v4/projects/123/security_settings")
# Use:
gl.http_get("/projects/123/security_settings")
Why This Bug Was Never Discovered
- Existing tests don't cover this edge case - they only test paths without
/api/v4/
prefix - Most developers use relative paths like
/projects/123
instead of/api/v4/projects/123
- The bug only manifests when someone explicitly uses paths starting with
/api/v4/
- Test coverage gap in the existing test suite
Community Impact
This bug affects any code that tries to use paths starting with /api/v4/
with the python-gitlab library. While this might be an uncommon pattern, it's a valid use case that should work correctly.
Discovery: This bug was discovered during real-world usage when trying to enable GitLab secret detection features, demonstrating that it affects actual production code.
Next Steps
- Review this bug report and confirm the issue
- Test the proposed fix to ensure it resolves the problem
- Consider merging the fix to resolve this longstanding issue
- Add the test case to prevent regression
Environment
- Python Version: 3.13.5
- Operating System: macOS 24.6.0
- Package Manager: uv
- Test Framework: pytest 8.4.1
Additional Notes
- The bug only affects paths starting with
/api/v4/
- Full URLs work correctly
- Relative paths without
/api/v4/
work correctly - This is a longstanding bug, not a breaking change
- The bug affects all versions of python-gitlab from 5.0.0 onwards
Commitment to Contribute
I am committed to:
- ✅ Providing a complete fix for this bug
- ✅ Adding comprehensive test coverage to prevent regression
- ✅ Following the project's coding standards (black, isort, conventional commits)
- ✅ Ensuring all tests pass before submitting a pull request
- ✅ Maintaining backward compatibility with existing code
This bug has been a blocker for our production use case, and I'm eager to contribute the fix back to the community.