#3284·wiremock

Wiremock __admin with form data header fails without readable error

Author: TWiStErRobCreated Dec 29, 2025Updated Sep 16, 2026
Labelsenhancement

Proposal

We've been manually calling the admin interface, because we control the stubs from Swift. We had a really hard time figuring out why Wiremock keeps returning 500 when there's a % sign in the JSON body. We've only seen a 500 through a proxy, there was no additional info (see WM server log below). We've tried to set up a similar thing in Java and it "just worked".

After many hours of debugging, we noticed that the for some weird reason Swift added application/x-www-form-urlencoded so we were essentially sending:

bash
curl -i -X POST 'http://localhost:8089/__admin/mappings' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-raw '{"id":"93702552-962B-41DA-AD1F-56C932932D9E","request":{"method":"GET","urlPath":"\/foo\/bar"},"response":{"statusMessage":"OK","headers":{"Content-Type":"application\/json"},"body":"{\"baz\":\"qux %2\"}","status":200}}'

This resulted in the %2 being interpreted as a URL-encoding, which is clearly invalid. %20 worked fine.

With this knowledge we were able to reproduce the problem in Java, and got a stack trace. Looking at the code it's clear-ish that the decoding happens before the request is handed to Wiremock (an "interceptor" fails), so that's why there are no logs.

So my plea is to somehow improve the error handling here, so we get a stack trace on the server side, not just a quiet 500 on the client side.

References

Version: 3.13.1

docker compose
yaml
services:
  example.com:
    image: wiremock/wiremock:3.13.1
    command:
      - "--global-response-templating"
      - "--disable-gzip"
      - "--use-chunked-encoding"
      - "never"
      - "--verbose"
    container_name: example.com
    ports:
      - "9000:8080"
    volumes:
      - "./wiremock/example.com/__files:/home/wiremock/__files"
      - "./wiremock/example.com/mappings:/home/wiremock/mappings"
log
�██     ██ ██ ██████  ███████ ███    ███  ██████   ██████ ██   ██ 
�██     ██ ██ ██   ██ ██      ████  ████ ██    ██ ██      ██  ██  
�██  █  ██ ██ ██████  █████   ██ ████ ██ ██    ██ ██      █████   
�██ ███ ██ ██ ██   ██ ██      ██  ██  ██ ██    ██ ██      ██  ██  
� ███ ███  ██ ██   ██ ███████ ██      ██  ██████   ██████ ██   ██
 
----------------------------------------------------------------
|               Cloud: https://wiremock.io/cloud               |
|                                                              |
|               Slack: https://slack.wiremock.org/              |
----------------------------------------------------------------
 
version:                      3.13.1
port:                         8080
enable-browser-proxying:      false
disable-banner:               false
no-request-journal:           false
verbose:                      true
 
extensions:                   response-template,webhook
2025-12-24 14:53:18.066 Admin request received:
10.89.2.16 - GET /health
 
Host: [localhost:9000]
Accept: [*/*]
User-Agent: [DemoUITests-Runner/1 CFNetwork/3826.500.131 Darwin/24.6.0]
Accept-Language: [en-GB,en;q=0.9]
Accept-Encoding: [gzip, deflate]
Connection: [keep-alive]
 
 
2025-12-24 14:53:18.092 Admin request received:
10.89.2.16 - POST /reset
 
Host: [localhost:9000]
Connection: [keep-alive]
Accept: [*/*]
User-Agent: [DemoUITests-Runner/1 CFNetwork/3826.500.131 Darwin/24.6.0]
Accept-Language: [en-GB,en;q=0.9]
Content-Length: [0]
Accept-Encoding: [gzip, deflate]

======== NOTHING HERE (this is where the 500 was received on the client side =========
 
2025-12-24 14:53:18.286 Admin request received:
10.89.2.16 - POST /shutdown
 
Host: [localhost:9000]
Connection: [keep-alive]
Accept: [*/*]
User-Agent: [DemoUITests-Runner/1 CFNetwork/3826.500.131 Darwin/24.6.0]
Accept-Language: [en-GB,en;q=0.9]
Content-Length: [0]
Accept-Encoding: [gzip, deflate]
Java server stack trace
[main] INFO org.eclipse.jetty.server.Server - jetty-11.0.24; built: 2024-08-26T18:11:22.448Z; git: 5dfc59a691b748796f922208956bd1f2794bcd16; jvm 21.0.8+-14196175-b1038.72
[main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.ServletContextHandler@26dcd8c0{/__admin,null,AVAILABLE}
[main] INFO org.eclipse.jetty.server.handler.ContextHandler.ROOT - RequestHandlerClass from context returned com.github.tomakehurst.wiremock.http.StubRequestHandler. Normalized mapped under returned 'null'
[main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.ServletContextHandler@7aad3f7d{/,null,AVAILABLE}
[main] INFO org.eclipse.jetty.server.AbstractConnector - Started NetworkTrafficServerConnector@241a0c3a{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:8089}
[main] INFO org.eclipse.jetty.server.Server - Started Server@5486887b{STARTING}[11.0.24,sto=1000] @625ms
[qtp1756160273-50] INFO org.eclipse.jetty.server.handler.ContextHandler.__admin - RequestHandlerClass from context returned com.github.tomakehurst.wiremock.http.AdminRequestHandler. Normalized mapped under returned 'null'
[qtp1756160273-50] WARN org.eclipse.jetty.server.HttpChannel - /__admin/mappings
java.lang.IllegalArgumentException: Not valid encoding '%2\'
  at org.eclipse.jetty.util.UrlEncoded.decodeHexByte(UrlEncoded.java:842)
  at org.eclipse.jetty.util.UrlEncoded.decodeUtf8To(UrlEncoded.java:305)
  at org.eclipse.jetty.util.UrlEncoded.decodeTo(UrlEncoded.java:183)
  at org.eclipse.jetty.util.UrlEncoded.decodeTo(UrlEncoded.java:166)
  at com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter.getFormParameters(WireMockHttpServletRequestAdapter.java:334)
  at com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter.<init>(WireMockHttpServletRequestAdapter.java:69)
  at com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet.service(WireMockHandlerDispatchingServlet.java:149)
  at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
  at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
  at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
  at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:314)
  at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:267)
  at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
  at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
  at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
  at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
  at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)
  at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
  at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
  at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
  at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)
  at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
  at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:141)
  at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
  at org.eclipse.jetty.server.Server.handle(Server.java:563)
  at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
  at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
  at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
  at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
  at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
  at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
  at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
  at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
  at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
  at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
  at java.base/java.lang.Thread.run(Unknown Source)
Exception in thread "main" java.lang.Exception: Response{protocol=http/1.1, code=500, message=Server Error, url=http://localhost:8089/__admin/mappings}
  at MainKt.makeRequest2(main.kt:78)
  at MainKt.main(main.kt:42)
  at MainKt.main(main.kt)