{"record":{"id":"0fb7f112385e3217","repo":"didi/DoKit","slug":"method-does-not-support-writing","errorCode":null,"errorMessage":"${method} does not support writing","messagePattern":"(.+?) does not support writing","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java","lineNumber":558,"sourceCode":"                    .build();\n        }\n\n        @Override\n        public int getReadTimeout() {\n            return client.readTimeoutMillis();\n        }\n\n        private Call buildCall() throws IOException {\n            if (call != null) {\n                return call;\n            }\n\n            connected = true;\n            if (doOutput) {\n                if (method.equals(\"GET\")) {\n                    method = \"POST\";\n                } else if (!permitsRequestBody(method)) {\n                    throw new ProtocolException(method + \" does not support writing\");\n                }\n            }\n\n            if (requestHeaders.get(\"User-Agent\") == null) {\n                requestHeaders.add(\"User-Agent\", defaultUserAgent());\n            }\n\n            OutputStreamRequestBody requestBody = null;\n            if (permitsRequestBody(method)) {\n                String contentType = requestHeaders.get(\"Content-Type\");\n                if (contentType == null) {\n                    contentType = \"application/x-www-form-urlencoded\";\n                    requestHeaders.add(\"Content-Type\", contentType);\n                }\n\n                boolean stream = fixedContentLength != -1L || chunkLength > 0;\n\n                long contentLength = -1L;","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java#L540-L576","documentation":"Inside buildCall(), when doOutput is true the wrapper promotes GET to POST, but for any other method that does not permit a request body it throws ProtocolException(method + \" does not support writing\"). permitsRequestBody() only allows methods with bodies (POST/PUT/PATCH...), so e.g. HEAD/DELETE/TRACE with doOutput=true fails here.","triggerScenarios":"setDoOutput(true) combined with setRequestMethod(\"HEAD\"|\"DELETE\"|\"TRACE\"|\"OPTIONS\"), then triggering the connection via connect()/getOutputStream()/getInputStream().","commonSituations":"A generic request builder that always calls setDoOutput(true) 'to be safe', later used with a DELETE endpoint; REST clients migrating a POST endpoint to DELETE without dropping doOutput.","solutions":["Only call setDoOutput(true) when you actually send a body with a body-permitting method.","Set doOutput(false) (default) for DELETE/HEAD/OPTIONS requests.","Move DELETE parameters into the query string or switch the endpoint to POST semantics if a body is required."],"exampleFix":"// before\nconn.setRequestMethod(\"DELETE\");\nconn.setDoOutput(true); // later -> DELETE does not support writing\n\n// after\nconn.setRequestMethod(\"DELETE\");\n// no setDoOutput; params in URL:\nurl += \"?id=\" + id;","handlingStrategy":"validation","validationCode":"if (needsBody) conn.setDoOutput(true); // else leave default false","typeGuard":"static boolean methodSupportsWriting(String m) {\n  return m.equals(\"GET\") || m.equals(\"POST\") || m.equals(\"PUT\") || m.equals(\"PATCH\");\n}","tryCatchPattern":null,"preventionTips":["setDoOutput(true) only on body-carrying requests.","For DELETE, put identifying data in the URL or switch to POST+override header."],"tags":["network","http","urlconnection","request-body"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}