qBittorrent
qBittorrent copied to clipboard
After upgrade to qBittorrent v4.5.0, now get BadRequest when I delete torrents through API
qBittorrent & operating system versions
qBittorrent: 4.5.0 x64, official build from website, no mods of any type Operating system: Windows 10 Pro 21H2 build 19044.2251
What is the problem?
This worked fine on prior version (unknown version, I last upgraded 2 months ago, so whatever version that was)
Here is my code. I've tried both POST and GET. It should be POST, per documentation, but I've tried GET to cover bases.
Again, this worked perfectly yesterday. I upgraded to v4.5.0 today, and now it fails with 'BadRequest' every time.
I can delete this torrent just fine from the qBittorrent console
var method = HttpMethod.Post;
var url = "http://127.0.0.1:8080/api/v2/torrents/delete?hashes=a6383161d63323a7cef9606c0769374a3b047d54&deleteFiles=false";
using (var myRequest = new HttpRequestMessage(method, url)
{
var myResponse = await httpClient.SendAsync(myRequest);
if (myResponse.IsSuccessStatusCode)
{
using (var myStream = new MemoryStream())
{
myResponse.Content.ReadAsStream().CopyTo(myStream);
return Encoding.Default.GetString(myStream.ToArray());
}
}
else
{
return $"{myResponse.StatusCode} - {myResponse.ReasonPhrase} on {apiName}/{methodName}{query} method {method.Method}";
}
}
Steps to reproduce
It's an API that I wrote. I shared the code above. Not sure what else I can do?
Additional context
I'd be happy to share logs or any additional info if needed, please ask.
Log(s) & preferences file(s)
Nothing sensitive in here.
Config.zip
(N) 2022-11-29T00:47:08 - Enqueued torrent move. Torrent: "SomeFile". Source: "D:\BT\qInProgress". Destination: "D:\BT\Completed"
(N) 2022-11-29T00:47:08 - Start moving torrent. Torrent: "SomeFile". Destination: "D:\BT\Completed"
(N) 2022-11-29T00:47:09 - Moved torrent successfully. Torrent: "SomeFile". Destination: "D:\BT\Completed"
(N) 2022-11-29T00:47:09 - Torrent download finished. Torrent: "SomeFile"
(N) 2022-11-29T00:47:09 - Running external program. Torrent: "SomeFile". Command: d:\bt\PlexBatch\PlexWorker.Batch.exe TorrentFinished "SomeFile" "D:\BT\Completed\SomeFile" "SomeFile" "D:\BT\Completed" "a6383161d63323a7cef9606c0769374a3b047d54" "-" "a6383161d63323a7cef9606c0769374a3b047d54"
Inside of PlexWorker.Batch.Exe, I do the call above, which results in the BadRequest error There is NOTHING in the log indicating anything to do with my API delete.
I have the same issue
I have a similar issue. I have posted the details on case# 18132
+1
Same problem
Getting Method not allowed
Switching to post worked for me
curl -d "hashes=$HASH&deleteFiles=true" -X POST "$QBITURL/api/v2/torrents/delete" --cookie "$COOKIE"
Starting with v4.5 many WebAPI endpoints were restricted to POST method only.
Indeed, I had the same issue and changed it to a post with the parameters in the body.
Powershell code example:
$hashesToDeleteString = "1a61bb4aeec95bdb9bb1082c67d24cc1bf66009c|1a61bb4aeec95bdb9bb1082c67d24cc1bf660045"
$body = @{
"hashes"=$hashesToDeleteString
"deleteFiles"="false"
}
Invoke-RestMethod -Uri "http://localhost:8080/api/v2/torrents/delete" -Method Post -Body $body
I am using POST
On Sat, Dec 3, 2022 at 10:33 AM Vladimir Golovnev @.***> wrote:
Starting with v4.5 many WebAPI endpoints were restricted to POST method only.
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336194151, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6BWUDG7BJYWIOBQAZLWLNY4RANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
url = 'api/v2/torrents/delete?hashes='+str(tor['hash'])+'&deleteFiles=true'
Sorry, how would I code this to use POST?
That depends on the language you use.
POST v GET is a 'method' used to transfer data from your app to the server (qBittorrent in this case). There are many methods, including PATCH, DELETE, etc. You would always specify a method in the transfer.
As a 'for example', I use C#. The method is the first parameter I pass. new HttpRequestMessage(method,
On Sat, Dec 3, 2022 at 4:10 PM Harry Storey @.***> wrote:
url = 'api/v2/torrents/delete?hashes='+str(tor['hash'])+'&deleteFiles=true'
Sorry, how would I code this to use POST?
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336267384, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
That depends on the language you use. POST v GET is a 'method' used to transfer data from your app to the server (qBittorrent in this case). There are many methods, including PATCH, DELETE, etc. You would always specify a method in the transfer. As a 'for example', I use C#. The method is the first parameter I pass. new HttpRequestMessage(method, … On Sat, Dec 3, 2022 at 4:10 PM Harry Storey @.> wrote: url = 'api/v2/torrents/delete?hashes='+str(tor['hash'])+'&deleteFiles=true' Sorry, how would I code this to use POST? — Reply to this email directly, view it on GitHub <#18097 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.>
Sorry I am using Python script
I've been using a POST, so that's not the issue. I changed from URL parms to Body parms. No difference. Still getting a BADMETHOD
On Sat, Dec 3, 2022 at 11:23 AM francisdidden @.***> wrote:
Indeed, I had the same issue and changed it to a post with the parameters in the body.
Powershell code example: $hashesToDeleteString = "1a61bb4aeec95bdb9bb1082c67d24cc1bf66009c|1a61bb4aeec95bdb9bb1082c67d24cc1bf660045" $body = @{ "hashes"=$hashesToDeleteString "deleteFiles"="false" } Invoke-RestMethod -Uri "http://localhost:8080/api/v2/torrents/delete" -Method Post -Body $body
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336202282, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6GNI2MGK2JRURSRLW3WLN6YNANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Here's an explanation of how a POST is done.
https://www.w3schools.com/python/ref_requests_post.asp
On Sat, Dec 3, 2022 at 4:15 PM Harry Storey @.***> wrote:
That depends on the language you use. POST v GET is a 'method' used to transfer data from your app to the server (qBittorrent in this case). There are many methods, including PATCH, DELETE, etc. You would always specify a method in the transfer. As a 'for example', I use C#. The method is the first parameter I pass. new HttpRequestMessage(method, … <#m_-7651487754727070193_> On Sat, Dec 3, 2022 at 4:10 PM Harry Storey @.> wrote: url = 'api/v2/torrents/delete?hashes='+str(tor['hash'])+'&deleteFiles=true' Sorry, how would I code this to use POST? — Reply to this email directly, view it on GitHub <#18097 (comment) https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336267384>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.>
Sorry I am using Python script
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336268099, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6CMF742E7AJBYGQIBDWLPA7RANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Here's an explanation of how a POST is done. https://www.w3schools.com/python/ref_requests_post.asp On Sat, Dec 3, 2022 at 4:15 PM Harry Storey @.> wrote: … That depends on the language you use. POST v GET is a 'method' used to transfer data from your app to the server (qBittorrent in this case). There are many methods, including PATCH, DELETE, etc. You would always specify a method in the transfer. As a 'for example', I use C#. The method is the first parameter I pass. new HttpRequestMessage(method, … <#m_-7651487754727070193_> On Sat, Dec 3, 2022 at 4:10 PM Harry Storey @.> wrote: url = 'api/v2/torrents/delete?hashes='+str(tor['hash'])+'&deleteFiles=true' Sorry, how would I code this to use POST? — Reply to this email directly, view it on GitHub <#18097 (comment) <#18097 (comment)>>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE https://github.com/notifications/unsubscribe-auth/AAXDH6FI2JMGHPFVQI5QYSTWLPALTANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.> Sorry I am using Python script — Reply to this email directly, view it on GitHub <#18097 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6CMF742E7AJBYGQIBDWLPA7RANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.**>
Thank you!
thanks everyone. c#: moving from GET to POST worked for me: { bc = new FormUrlEncodedContent( { new KeyValuePair<string, string>("hashes", hash), new KeyValuePair<string, string>("deleteFiles", "false") }); hrm = hc.PostAsync("/api/v2/torrents/delete", bc).Result; result = hrm.Content.ReadAsStringAsync.Result; if (hrm.IsSuccessStatusCode) pass = true; }
This still does not work for me, I've been doing a POST the entire time.
On Sun, Dec 4, 2022 at 7:17 PM grtgh @.***> wrote:
thanks everyone. c#: moving from GET to POST worked for me: { bc = new FormUrlEncodedContent( { new KeyValuePair<string, string>("hashes", hash), new KeyValuePair<string, string>("deleteFiles", "false") }); hrm = hc.PostAsync("/api/v2/torrents/delete", bc).Result; result = hrm.Content.ReadAsStringAsync.Result; if (hrm.IsSuccessStatusCode) pass = true; }
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1336593743, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6DVEF5YUDLKEKXWO63WLU7A3ANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
I would presume you donnt have authentication because you didnt pass a cookie?
Did you try a simple curl request just to test? or something like postman?
Im not familiar with the language.
I turned off authentication for localhost, so I don't need the cookie.
On Mon, Dec 5, 2022 at 8:19 PM jobrien2001 @.***> wrote:
I would presume you donnt have authentication because you didnt pass a cookie?
Did you try a simple curl request just to test? or something like postman?
Im not familiar with the language.
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1338634829, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6FYWMDCXQO7ZZ7FWT3WL2PERANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Whats odd is that you were getting a bad request error...
I think we were all getting "method not allowed". Bad request would mean something is malformed?
I would start looking at running something simple like curl like i suggested before, just to test things out.
var method = HttpMethod.Post; var url = "http://127.0.0.1:8080/api/v2/torrents/delete?hashes=a6383161d63323a7cef9606c0769374a3b047d54&deleteFiles=false";
Doesn't seem like correct thing. Is query string allowed to be in URL when method is POST?
First off, yes, I do it all the time, it's VERY common. Secondly, I've tried it both ways, query string and body.
On Mon, Dec 5, 2022 at 9:38 PM Vladimir Golovnev @.***> wrote:
var method = HttpMethod.Post; var url = " http://127.0.0.1:8080/api/v2/torrents/delete?hashes=a6383161d63323a7cef9606c0769374a3b047d54&deleteFiles=false ";
Doesn't seem like correct thing. Is query string allowed to be in URL when method is POST?
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1338695978, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6DKA2I767LA7RLL7H3WL2YMVANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Did anyone get this working with python?
I use C#, I've never gotten it to work.
On Sat, Dec 10, 2022 at 1:16 PM Harry Storey @.***> wrote:
Did anyone get this working with python?
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1345365675, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6AKX5VXMC7IL7KXHD3WMTJHBANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
same problem when I send with GET method that server return Method Not Allowed. When I change into POST then it said Bad Request, I already change to DELETE still bad request. I am using Postman with Authend is off
@qbittorrent/bug-handlers Could someone investigate it?
Because qbittorrent try reading data from body instead of query. In my case, this code block is working:
import urllib
req = urllib.request.Request(
url="http://host:port/api/v2/torrents/pause",
method="POST",
data=urllib.parse.urlencode({"hashes": "your_hashes"}).encode(encoding="utf8"),
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
and delete torrent:
req = urllib.request.Request(
url="http://host:port/api/v2/torrents/delete",
method="POST",
data=urllib.parse.urlencode({"hashes": "your_hashes", "deleteFiles": "false"}).encode(encoding="utf8"),
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
I still cannot get the DELETE to work, even making your suggested changes.
It still throws back 'BAD REQUEST' and does not delete.
Note, you are doing a PAUSE, not a DELETE.
Pause works fine for me.
On Sat, Jan 14, 2023 at 11:51 AM 与田祐希是真的天下第一 @.***> wrote:
Because qbittorrent try read data from body instead of query. In my case, this code block is working (Generated by Insomnia):
var client = new RestClient("http://host:port/api/v2/torrents/pause"); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/x-www-form-urlencoded"); request.AddCookie("SID", "authkey"); request.AddParameter("application/x-www-form-urlencoded", "hashes=yourhash", ParameterType.RequestBody); IRestResponse response = client.Execute(request);
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1382870152, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6E3MCLA2LMI7OPEWN3WSLRS3ANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Works fine with built-in webUI.
Both PAUSE and DELETE are working properly in my project. In fact, in new version webui api code 400 (BAD REQUEST) seems to denote any arguments error or even some server internal error (should be code 500).
If the C# code block does not work, please check my new reply of python version, the C# version is generated by Insomnia automatically (the request is properly working by sending request directly in Insomnia, which means the request is valid). The keypoint might be proper data encoding format.
Brian Kitt @.***> 于2023年1月17日周二 11:21写道:
I still cannot get the DELETE to work, even making your suggested changes.
It still throws back 'BAD REQUEST' and does not delete.
Note, you are doing a PAUSE, not a DELETE.
Pause works fine for me.
On Sat, Jan 14, 2023 at 11:51 AM 与田祐希是真的天下第一 @.***> wrote:
Because qbittorrent try read data from body instead of query. In my case, this code block is working (Generated by Insomnia):
var client = new RestClient("http://host:port/api/v2/torrents/pause"); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/x-www-form-urlencoded"); request.AddCookie("SID", "authkey"); request.AddParameter("application/x-www-form-urlencoded", "hashes=yourhash", ParameterType.RequestBody); IRestResponse response = client.Execute(request);
— Reply to this email directly, view it on GitHub < https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1382870152 , or unsubscribe < https://github.com/notifications/unsubscribe-auth/AAXDH6E3MCLA2LMI7OPEWN3WSLRS3ANCNFSM6AAAAAASN4SFUE
. You are receiving this because you authored the thread.Message ID: @.***>
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1384785241, or unsubscribe https://github.com/notifications/unsubscribe-auth/AH7NIHW7VWTVOZNQP7ZYI5DWSYF4VANCNFSM6AAAAAASN4SFUE . You are receiving this because you commented.Message ID: @.***>
I believe I have identified the root cause of this problem.
The documentation clearly states that the delete call should be this: /api/v2/torrents/delete?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32&deleteFiles=false RE: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#delete-torrents That is incorrect.
The way this works is as follows. The URL must be /api/v2/torrents/delete Then the parameters MUST be form variables, NOT JSON, but form variables. If you sent it as form variables like this. then it works: hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32 deleteFiles=false
On Tue, Jan 17, 2023 at 3:39 AM 与田祐希是真的天下第一 @.***> wrote:
Both PAUSE and DELETE are working properly in my project. In fact, in new version webui api code 400 (BAD REQUEST) seems to denote any arguments error or even some server internal error (should be code 500).
If the C# code block does not work, please check my new reply of python version, the C# version is generated by Insomnia automatically (the request is properly working by sending request directly in Insomnia, which means the request is valid). The keypoint might be proper data encoding format.
Brian Kitt @.***> 于2023年1月17日周二 11:21写道:
I still cannot get the DELETE to work, even making your suggested changes.
It still throws back 'BAD REQUEST' and does not delete.
Note, you are doing a PAUSE, not a DELETE.
Pause works fine for me.
On Sat, Jan 14, 2023 at 11:51 AM 与田祐希是真的天下第一 @.***> wrote:
Because qbittorrent try read data from body instead of query. In my case, this code block is working (Generated by Insomnia):
var client = new RestClient("http://host:port/api/v2/torrents/pause"); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/x-www-form-urlencoded"); request.AddCookie("SID", "authkey"); request.AddParameter("application/x-www-form-urlencoded", "hashes=yourhash", ParameterType.RequestBody); IRestResponse response = client.Execute(request);
— Reply to this email directly, view it on GitHub <
https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1382870152
, or unsubscribe <
https://github.com/notifications/unsubscribe-auth/AAXDH6E3MCLA2LMI7OPEWN3WSLRS3ANCNFSM6AAAAAASN4SFUE
. You are receiving this because you authored the thread.Message ID: @.***>
— Reply to this email directly, view it on GitHub < https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1384785241 , or unsubscribe < https://github.com/notifications/unsubscribe-auth/AH7NIHW7VWTVOZNQP7ZYI5DWSYF4VANCNFSM6AAAAAASN4SFUE
. You are receiving this because you commented.Message ID: @.***>
— Reply to this email directly, view it on GitHub https://github.com/qbittorrent/qBittorrent/issues/18097#issuecomment-1385103531, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAXDH6G4BAWLNDDD7VB75RLWSZSEPANCNFSM6AAAAAASN4SFUE . You are receiving this because you authored the thread.Message ID: @.***>
Thank you @briankitt for working this out. You have also solved this problem for me. The WebAPI documentation for pause and resume operations is also outdated and incorrect. Changing the pause/resume API calls to use form variables works.
eg:
HASHSTR="hash1|hash2|hash3"
curl -v -b "SID=${SID}" -X POST -F "hashes=${HASHSTR}" http://${QBTHOST}/api/v2/torrents/pause
curl -v -b "SID=${SID}" -X POST -F "hashes=${HASHSTR}" http://${QBTHOST}/api/v2/torrents/resume
I have the same issue, I using Javascript ajax on tampermonkey of chrome, the browser console displays: 400 (Bad Request) my code is:
let url = '/api/v2/torrents/delete?hashes=xxxxxxxxxx&deleteFiles=true',
xhrDelete = new XMLHttpRequest();
xhrDelete.open('POST', url, true);
xhrDelete.send();
xhrDelete.onreadystatechange = function () {
if(xhrDelete.readyState == 4 && ((xhrDelete.status >= 200 && xhrDelete.status < 300) || xhrDelete.status == 304)){
some code...
}
}
update: Today I change my code to jQuery ajax, it worked and I don't know why.