didi/DoKit · error · ProtocolException

${method} does not support writing

Error message

${method} does not support writing

What it means

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.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java:558

                    .build();
        }

        @Override
        public int getReadTimeout() {
            return client.readTimeoutMillis();
        }

        private Call buildCall() throws IOException {
            if (call != null) {
                return call;
            }

            connected = true;
            if (doOutput) {
                if (method.equals("GET")) {
                    method = "POST";
                } else if (!permitsRequestBody(method)) {
                    throw new ProtocolException(method + " does not support writing");
                }
            }

            if (requestHeaders.get("User-Agent") == null) {
                requestHeaders.add("User-Agent", defaultUserAgent());
            }

            OutputStreamRequestBody requestBody = null;
            if (permitsRequestBody(method)) {
                String contentType = requestHeaders.get("Content-Type");
                if (contentType == null) {
                    contentType = "application/x-www-form-urlencoded";
                    requestHeaders.add("Content-Type", contentType);
                }

                boolean stream = fixedContentLength != -1L || chunkLength > 0;

                long contentLength = -1L;

View on GitHub (pinned to 626827cddb)

Solutions

  1. Only call setDoOutput(true) when you actually send a body with a body-permitting method.
  2. Set doOutput(false) (default) for DELETE/HEAD/OPTIONS requests.
  3. Move DELETE parameters into the query string or switch the endpoint to POST semantics if a body is required.

Example fix

// before
conn.setRequestMethod("DELETE");
conn.setDoOutput(true); // later -> DELETE does not support writing

// after
conn.setRequestMethod("DELETE");
// no setDoOutput; params in URL:
url += "?id=" + id;
Defensive patterns

Strategy: validation

Validate before calling

if (needsBody) conn.setDoOutput(true); // else leave default false

Type guard

static boolean methodSupportsWriting(String m) {
  return m.equals("GET") || m.equals("POST") || m.equals("PUT") || m.equals("PATCH");
}

Prevention

When it happens

Trigger: setDoOutput(true) combined with setRequestMethod("HEAD"|"DELETE"|"TRACE"|"OPTIONS"), then triggering the connection via connect()/getOutputStream()/getInputStream().

Common situations: 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.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/0fb7f112385e3217. Report an issue: GitHub.