{"id":25560,"date":"2026-09-05T23:17:39","date_gmt":"2026-09-05T23:17:39","guid":{"rendered":"https:\/\/capskip.com\/?p=25560"},"modified":"2026-09-05T23:17:39","modified_gmt":"2026-09-05T23:17:39","slug":"error-wrong-user-key","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/error-wrong-user-key\/","title":{"rendered":"\u5982\u4f55\u4fee\u590d ERROR_WRONG_USER_KEY \u53ca\u53e6\u5916 3 \u4e2a\u8bf7\u6c42\u9519\u8bef"},"content":{"rendered":"<p>error_wrong_user_key means your API key never left your code. That is the whole diagnosis, and it is worth stating up front because almost everyone reads it as &#8220;my key is wrong&#8221; and starts pasting a new one. CapSkip has a different code for a key that is wrong. error_wrong_user_key, and the three that sit beside it, are returned before the solver has looked at a CAPTCHA at all: they describe the shape of the HTTP request you sent, not the challenge it was about.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The four codes, and the one they get confused with<\/h2>\n<p>These come back from the submit endpoint or the poll endpoint as plain text, in place of the usual OK reply. Every one of them is deterministic. Retrying an identical request produces an identical answer, so a retry loop around any of these is wasted time. The second row below is the sibling code that is not one of the four, because telling those two apart is most of the work.<\/p>\n<table>\n<thead>\n<tr>\n<th>Code<\/th>\n<th>Official meaning<\/th>\n<th>What it means in practice<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>ERROR_WRONG_USER_KEY<\/td>\n<td>API key missing or empty<\/td>\n<td>The key parameter was absent, or present with an empty value<\/td>\n<\/tr>\n<tr>\n<td>ERROR_KEY_DOES_NOT_EXIST<\/td>\n<td>Invalid API key<\/td>\n<td>A key arrived, and CapSkip does not recognise it<\/td>\n<\/tr>\n<tr>\n<td>ERROR_WRONG_METHOD<\/td>\n<td>Invalid HTTP method or action parameter<\/td>\n<td>The method value is not one CapSkip knows, or action is not get<\/td>\n<\/tr>\n<tr>\n<td>ERROR_WRONG_ID_FORMAT<\/td>\n<td>Invalid captcha ID format<\/td>\n<td>The id you polled with is not a bare number<\/td>\n<\/tr>\n<tr>\n<td>ERROR_BAD_PARAMETERS<\/td>\n<td>Missing or invalid required parameters<\/td>\n<td>A required field for that CAPTCHA type is absent or malformed<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The full list, including the image upload codes and the reCAPTCHA specific ones, is in <a href=\"https:\/\/capskip.com\/api-docs\/\">the CapSkip API documentation<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">ERROR_WRONG_USER_KEY: the key is absent, not incorrect<\/h2>\n<p>Missing and invalid are two different bugs with two different fixes, and CapSkip separates them on purpose. If a key reached the server and was not recognised, you get error_key_does_not_exist, which is written up in <a href=\"https:\/\/capskip.com\/error-key-does-not-exist\/\">the guide to that code<\/a>. If you get error_wrong_user_key instead, nothing recognisable arrived, so stop looking at the value and start looking at whether it was sent.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># No key parameter at all. This is what produces it.\r\ncurl -X POST \\\r\n  -d &quot;method=userrecaptcha&quot; \\\r\n  -d &quot;googlekey=YOUR_SITEKEY&quot; \\\r\n  -d &quot;pageurl=https:\/\/example.com\/page-with-recaptcha&quot; \\\r\n  http:\/\/127.0.0.1:8080\/in.php\r\n\r\nERROR_WRONG_USER_KEY<\/pre>\n<\/div>\n<p>Four things produce it, in rough order of how often they do:<\/p>\n<ul>\n<li>An environment variable that is not set where the code runs. The classic version of this is a shell that has it and a service that does not. The value reads as an empty string and the client sends key with nothing after it.<\/li>\n<li>A key read from a config file that was not deployed alongside the code.<\/li>\n<li>A hand rolled client that only adds key when a variable is truthy, so an empty string silently drops the parameter.<\/li>\n<li>A client that puts the parameters somewhere the request will not carry them, such as a JSON body on an endpoint that reads form fields.<\/li>\n<\/ul>\n<p>Print the value&#8217;s length before the call rather than the value. A length of zero tells you what you need and does not put a credential in a log file.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Why this only starts happening on a server<\/h2>\n<p>Here is the part that confuses people who have been running the same code for months. CapSkip requires the key only when API Key Validation is switched on in the app. On a fresh local install it is off, so a request with no key at all is accepted and nothing ever complains. Your client may have been sending an empty key since the day you wrote it.<\/p>\n<p>Then you move the solver onto a box that is not your desk. CapSkip has two connection modes: Local binds to 127.0.0.1 and answers that device only, while Server binds to your network or public IP so another machine, a VPS or a hosted worker can reach it over the API. A static public IP keeps the address stable. Both are described under <a href=\"https:\/\/capskip.com\/setup-guide\/#connection-settings\">connection settings<\/a>. Turning key validation on when you make that move is the right thing to do, and it is also the moment every latent key bug in your code surfaces at once. It is still your hardware and still unmetered either way, so this is a configuration change and not a change in what you pay.<\/p>\n<p>If you are handing keys out to several workers, give each one its own so a single key can be revoked without touching the rest. That side of it is covered in <a href=\"https:\/\/capskip.com\/provision-captcha-api-keys\/\">the guide to provisioning CAPTCHA API keys<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">ERROR_WRONG_ID_FORMAT: you probably sent the whole reply<\/h2>\n<p>This one has a single dominant cause and it is easy to spot once you know it. The submit endpoint does not answer with a bare number. It answers with OK and the id separated by a pipe, and a client that passes that string straight into the poll request sends an id that is not a number.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\"># What in.php actually returns:\r\nOK|212\r\n\r\n# Wrong. The whole reply went into id.\r\ncurl &quot;http:\/\/127.0.0.1:8080\/res.php?key=YOUR_API_KEY&amp;action=get&amp;id=OK|212&quot;\r\nERROR_WRONG_ID_FORMAT\r\n\r\n# Right. Split on the pipe and send the number.\r\ncurl &quot;http:\/\/127.0.0.1:8080\/res.php?key=YOUR_API_KEY&amp;action=get&amp;id=212&quot;<\/pre>\n<\/div>\n<p>Ask for JSON and the parsing gets less fragile, because the id arrives as a field rather than as half of a delimited string. Either way, check the prefix before you split, because an error code has no pipe in it and splitting blindly gives you back the error code as an id.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># A hand rolled client has to do this itself. The SDK does not,\r\n# which is most of why it is worth using.\r\nreply = httpx.post(IN_URL, data=payload).text     # &quot;OK|212&quot;\r\n\r\nif not reply.startswith(&quot;OK|&quot;):\r\n    raise RuntimeError(reply)                     # it is an error code\r\n\r\ncaptcha_id = reply.split(&quot;|&quot;, 1)[1]               # &quot;212&quot;<\/pre>\n<\/div>\n<p>A worked example of the raw request and poll cycle, with the same delimiter handling, is in <a href=\"https:\/\/capskip.com\/solve-captcha-curl\/\">the walkthrough of solving a CAPTCHA with curl<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">ERROR_WRONG_METHOD: the wrong verb, or the wrong word<\/h2>\n<p>Two separate mistakes share this code, which is why it reads as vague. The first is the HTTP verb: parameters sent as a form body on an endpoint you called with GET arrive nowhere. The second is the method parameter itself, which has to be one of the values CapSkip recognises for the type you are solving: post or base64 for an image, userrecaptcha for reCAPTCHA in either version, turnstile for Cloudflare, geetest for GeeTest v3.<\/p>\n<p>Two spellings account for most of it: sending sitekey to the reCAPTCHA endpoint, which wants googlekey, and sending recaptcha or userecaptcha instead of userrecaptcha. The same code also covers the poll side, where action has to be the literal string get.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">ERROR_BAD_PARAMETERS: the type and the fields disagree<\/h2>\n<p>The method was understood and something required for it was missing or malformed. It is per type, so the useful question is always which type CapSkip thinks you asked for.<\/p>\n<ul>\n<li>reCAPTCHA needs both googlekey and pageurl, and pageurl must be the full URL of the page the widget loads on, scheme included.<\/li>\n<li>reCAPTCHA v3 needs version set to v3. Sending invisible with it puts a v2 field on a v3 request.<\/li>\n<li>Turnstile challenge pages need data and pagedata as well as the sitekey. Widget mode needs neither.<\/li>\n<li>GeeTest needs gt and challenge together, and challenge expires in about a minute, so a stale one fails here rather than later.<\/li>\n<li>Image CAPTCHAs need file for a multipart upload or body for base64, and never both.<\/li>\n<\/ul>\n<p>Log the payload you are about to send, with the key redacted, and read it against the parameter table for that type. Nine times out of ten the answer is visible in that one line.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The empty response is not one of these<\/h2>\n<p>A poll that returns nothing at all is a different situation, and it is worth naming because an empty reply gets mistaken for a bad id. An empty body means the result was already retrieved, or the id does not exist. CapSkip lets you read a result once. A retry loop that succeeds, then loops again because the code forgot to break, reads an empty string the second time and reports a failure on a CAPTCHA that was solved correctly.<\/p>\n<p>Store the result the first time you get it. And do not confuse an empty response with CAPCHA_NOT_READY, which is a normal polling state rather than a failure, described in <a href=\"https:\/\/capskip.com\/capcha-not-ready\/\">the guide to that response<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Reading these from the SDK<\/h2>\n<p>Every one of the four codes arrives as an ApiException, with the code as the message. That is the exception to check for first, because it means CapSkip understood you and refused, which is a different class of problem from not being able to reach CapSkip at all.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\"># pip install capskip\r\nfrom capskip import CapSkip, ApiException, NetworkException\r\n\r\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080, apiKey=API_KEY)\r\n\r\ntry:\r\n    token = solver.recaptcha(sitekey=SITEKEY, url=PAGE_URL)[&quot;code&quot;]\r\nexcept ApiException as err:\r\n    # CapSkip answered and rejected the request. Do not retry.\r\n    print(&quot;Rejected:&quot;, err)\r\nexcept NetworkException as err:\r\n    # CapSkip did not answer. Wrong host, wrong port, or not running.\r\n    print(&quot;Unreachable:&quot;, err)<\/pre>\n<\/div>\n<p>ValidationException is worth knowing about here too, because it fires before anything is sent. The SDK rejects parameters that are wrong for the type you asked for, so a mistake that would have come back as ERROR_BAD_PARAMETERS over raw HTTP is caught locally instead, with a message that names the argument. The SDK has four exception types in all: ApiException, NetworkException, TimeoutException and ValidationException. Every one of them derives from a base called CapSkipError if you would rather handle them in one place. The same four exist in the Node.js, PHP and C# clients, which are listed on <a href=\"https:\/\/capskip.com\/captcha-solving-sdk\/\">the CAPTCHA solving SDK page<\/a>.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">FAQ<\/h2>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">What is the difference between ERROR_WRONG_USER_KEY and ERROR_KEY_DOES_NOT_EXIST?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Whether a key arrived. The first means the key parameter was missing or empty, so look at your configuration and your request builder. The second means a key arrived and CapSkip does not recognise it, so look at the value and at which key the app is actually issuing.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Do I need an API key at all?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Only when API Key Validation is switched on in the CapSkip app. It is off by default, which is fine on a machine where the solver only answers loopback. Switch it on before the solver listens on a network address, and send a key from then on.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Should I retry any of these?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. All four are deterministic, so the second attempt fails exactly like the first. Retry the transient failures instead: a connection error, a polling timeout, or a CAPTCHA that came back unsolvable. Retrying a malformed request just spends your backoff budget on a bug.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Everything worked locally and broke on the server. Why?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Almost always because key validation went on with the move, and the key was never really being sent. The second most common cause is an environment variable that exists in your shell but not in the service that runs the code. Check the length of the value at the point of the call.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The short version<\/h2>\n<p>These four codes are about your request, not about the CAPTCHA. error_wrong_user_key means nothing arrived in the key parameter, which is a configuration problem rather than a wrong value. ERROR_WRONG_ID_FORMAT almost always means the pipe delimited reply went in whole. ERROR_WRONG_METHOD means a wrong verb or a wrong spelling, and ERROR_BAD_PARAMETERS means a field that does not belong to the type you asked for. None of them is worth retrying.<\/p>\n<p>The parameter tables that settle every one of these are on <a href=\"https:\/\/capskip.com\/api-docs\/\">the API documentation page<\/a>. The Python client that removes most of the opportunity to get them wrong is on <a href=\"https:\/\/capskip.com\/python-captcha-solver\/\">the Python CAPTCHA solver page<\/a>.<\/p>\n<p>Worth remembering while you are debugging: CapSkip is a <a href=\"https:\/\/capskip.com\/\">captcha bypass<\/a> tool that runs on your own hardware, so a malformed request that you send a hundred times while working it out costs you nothing but the time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u6709\u56db\u4e2a CapSkip \u9519\u8bef\u7801\u662f\u5728\u8bc6\u522b\u5de5\u5177\u8fd8\u6ca1\u770b\u4e00\u773c\u9a8c\u8bc1\u7801\u4e4b\u524d\u5c31\u8fd4\u56de\u7684\u3002\u5b83\u4eec\u8bf4\u7684\u662f\u4f60\u7684 HTTP \u8bf7\u6c42\u672c\u8eab\u7684\u683c\u5f0f\uff0c\u6bcf\u4e00\u4e2a\u6307\u5411\u7684\u9519\u8bef\u90fd\u4e0d\u4e00\u6837\u3002\u4e0b\u9762\u662f\u6bcf\u4e2a\u9519\u8bef\u7801\u7684\u542b\u4e49\u548c\u4fee\u6cd5\u3002<\/p>","protected":false},"author":1,"featured_media":25559,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"ERROR_WRONG_USER_KEY and 3 Request Errors | CapSkip","rank_math_description":"error_wrong_user_key means the API key never left your code. Here is how it differs from an invalid key, plus the three other request shape errors.","rank_math_focus_keyword":"error_wrong_user_key","footnotes":""},"categories":[70],"tags":[],"class_list":["post-25560","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-captcha"],"_links":{"self":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25560","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/comments?post=25560"}],"version-history":[{"count":2,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25560\/revisions"}],"predecessor-version":[{"id":25564,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/25560\/revisions\/25564"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/25559"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=25560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=25560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=25560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}