FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

project.files.save() fails due to start_branch being a URL query parameter and not a body attribute · Issue #3318 · python-gitlab/python-gitlab · GitHub

Repository navigation

project.files.save() fails due to start_branch being a URL query parameter and not a body attribute #3318

Description

Description of the problem, including code/CLI snippet

Attempting to modify a file and save it a new branch using the start_branch attribute

file = project.files.get(file_path=FILE_PATH, ref=project.default_branch)

file.content = "Modified contents"

file.save(
    start_branch=self.default_branch,
    branch=f"{NEW_BRANCH_NAME}",
    commit_message=f"File was modified for this new branch",
)

fails with with a 400 error GitlabUpdateError('You can only create or edit files when you are on a branch')

Enabling debugging its apparent that start_branch is being sent using a URL query string while everything else a body attribute.

send: b'PUT /api/v4/projects/12345678/repository/files/readme.txt?start_branch=main HTTP/1.1\r\nHost: gitlab.com\r\nUser-Agent: python-gitlab/7.1.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-type: application/json\r\nPRIVATE-TOKEN: glpat-HIDDEN_TOKEN\r\n'

send: b'{"file_path": "readme.txt", "branch": "new_branch", "content": "Modified contents", "commit_message": "File was modified for this new branch"}'

which would correspond to the following curl command:

$ curl --request PUT \
       --url "https://gitlab.com/api/v4/projects/12345678/repository/files/readme.txt?start_branch=main" \
       --header "PRIVATE-TOKEN: ${GITLAB_PRIVATE_TOKEN}" \
       --header "Content-Type: application/json" \
       --data '{"file_path": "readme.txt", "branch": "new_branch", "content": "Modified contents", "commit_message": "File was modified for this new branch"}'

{"message":"You can only create or edit files when you are on a branch"}

Not specifying start_branch fails in the same way. However in the curl command if start_branch is moved from being a URL query parameter to a body attribute then it succeeds:

$ curl --request PUT \
       --url "https://gitlab.com/api/v4/projects/12345678/repository/files/readme.txt" \
       --header "PRIVATE-TOKEN: ${GITLAB_PRIVATE_TOKEN}" \
       --header "Content-Type: application/json" \
       --data '{"file_path": "readme.txt", "start_branch": "main", "branch": "new_branch", "content": "Modified contents", "commit_message": "File was modified for this new branch"}'

{"file_path":"readme.txt","branch":"new_branch"}

So question is how is start_branch used with the python project.files.save() such that its passed as a body attribute and not a URL query parameter?

Expected Behavior

Able to update a file using project.files save() with start_branch attribute.

Actual Behavior

It fails with a 400 error GitlabUpdateError('You can only create or edit files when you are on a branch'), and it does not seem possible to use the save() api.

Links

Specifications

  • python-gitlab version: 7.1.0
  • Gitlab server version (or gitlab.com): gitlab.com

Activity

  1. JohnVillalovos commented on Jan 5, 2026

    Member

    Not sure best way to fix it, but why it is happening is because of

    def save( # type: ignore[override]
    self, branch: str, commit_message: str, **kwargs: Any
    ) -> None:
    """Save the changes made to the file to the server.
    The object is updated to match what the server returns.
    Args:
    branch: Branch in which the file will be updated
    commit_message: Message to send with the commit
    **kwargs: Extra options to send to the server (e.g. sudo)
    Raises:
    GitlabAuthenticationError: If authentication is not correct
    GitlabUpdateError: If the server cannot perform the request
    """
    self.branch = branch
    self.commit_message = commit_message
    self.file_path = utils.EncodedId(self.file_path)
    super().save(**kwargs)

    Both branch and commit_message are special cases. Maybe 'start_branch` should be added as an optional parameter?

  2. nickbroon commented on Jan 6, 2026

    ContributorAuthor

    Perhaps like this?

    index 757d16ee..34a853d8 100644
    --- i/gitlab/v4/objects/files.py
    +++ w/gitlab/v4/objects/files.py
    @@ -26,6 +26,7 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
         _repr_attr = "file_path"
         branch: str
         commit_message: str
    +    start_branch: str | None = None
         file_path: str
         manager: ProjectFileManager
         content: str  # since the `decode()` method uses `self.content`
    @@ -41,7 +42,11 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
         # NOTE(jlvillal): Signature doesn't match SaveMixin.save() so ignore
         # type error
         def save(  # type: ignore[override]
    -        self, branch: str, commit_message: str, **kwargs: Any
    +        self,
    +        branch: str,
    +        commit_message: str,
    +        start_branch: str | None = None,
    +        **kwargs: Any
         ) -> None:
             """Save the changes made to the file to the server.
    
    @@ -50,6 +55,7 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
             Args:
                 branch: Branch in which the file will be updated
                 commit_message: Message to send with the commit
    +            start_branch: Name of the branch to start the new branch from
                 **kwargs: Extra options to send to the server (e.g. sudo)
    
             Raises:
    @@ -58,6 +64,7 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
             """
             self.branch = branch
             self.commit_message = commit_message
    +        self.start_branch = start_branch
             self.file_path = utils.EncodedId(self.file_path)
             super().save(**kwargs)
    
  3. added 2 commits that reference this issue on Jan 20, 2026
    6d86c8b
    ad2802a
  4. added a commit that references this issue on Jan 20, 2026
    1001d93
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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


      Back | FazBrowse Home | New Git URL