The Receiver That Returned 200

On the Marantz in the living room, the query string I should not have sent, and the five minutes of recovery time that followed


The Marantz SR7010 in the living room stopped responding at 9:47 PM on a Thursday. I had been piping a different input to it from a small Python script — the kind of one-liner I write to get out of the way of a task I am about to forget. The script’s third request went out as a GET with a query string I had typed by hand. The query string was wrong. I knew it was wrong before I sent it. I sent it anyway.

The receiver returned 200 OK. That is the part that took five minutes to figure out.

What the vendor API actually wants

The Marantz has a vendor web control API on port 8080. The endpoint that reports state is http://192.168.1.170:8080/goform/formMainZone_MainZoneXml.xml, and the one that takes a command is http://192.168.1.170:8080/goform/formiPhoneAppDirect.xml. The control endpoint wants a POST. The state endpoint wants a GET with no query string. The vendor docs say this in three pages of PDF, and they say it once.

The query string I typed was ?Zone1=Main. I had copied it from an old curl command in a different file. It was meant for a Yamaha receiver from a project that did not ship. I sent it to the Marantz because the two boxes share the same /goform prefix and look similar from the outside. They are not similar from the inside.

The state endpoint does not read query strings. It does not error on them either. It just goes silent.

What silent looks like

After the bad GET, every subsequent request to the receiver returned 200 with an empty body. The Python requests library counted this as success. My script printed OK and moved on. The lights on the receiver did not change. The input did not switch. The script tried again. It waited. It retried. The receiver kept returning 200.

The vendor recovery is documented: power-cycle the unit. Hold the power button on the front panel for ten seconds. Unplug the IEC cable for thirty seconds. The receiver’s web control stack is a small Linux process on the same SoC that runs the HDMI board. When the state process wedges, the HTTP listener still answers. The HTTP listener lies. The lie takes five minutes to undo, and the only way to undo it is the cable.

What I tried before the cable

I waited first. The receiver had wedged twice before in this project, and both times it had cleared itself inside two minutes. Two minutes turned into three, then into five.

I sent the command I actually wanted, as a POST, with the right form payload. The 200 came back the first time. The lights did not change. The 200 came back the second time. The lights still did not change.

I curled the state endpoint from a different machine on the LAN. Same 200, empty body. A different vendor endpoint — formNetAudio.xml, which reports DLNA state — returned the same 200, the same empty body. Whatever had wedged had wedged across the whole /goform tree.

I ran nmap against the box from the Linux box. Port 8080 was open. Port 80 was open. Port 8080 answered TCP but did not parse HTTP. The TCP handshake finished in 2 ms. The HTTP response came back as soon as I closed the connection, which is what nc does, which is what the vendor stack was now doing. The vendor stack had stopped parsing.

The recovery is the cable

I walked to the living room. I held the power button on the front panel for ten seconds. The display went out, then came back. I unplugged the IEC cable. I waited thirty seconds. I plugged it back in. The receiver went through its boot sequence. The web control stack came back up at the thirty-second mark. I curled the state endpoint from the kitchen. The body came back. It had the input name. The lights on the receiver changed when I sent the next command.

The five minutes were over. They had been over in the thirty seconds I spent holding the power button. The other four and a half minutes were the script retrying, me curling from different machines, and me reading the vendor docs for the third time to see if I had missed a recovery endpoint. There is no recovery endpoint. There is the cable.

What the next script looks like

The script that caused this is on the Linux box. I have rewritten it. The wrapper checks the response body for an XML payload before counting the request as a success. A 200 with an empty body is a failure. The wrapper rate-limits to one request per second. The Marantz’s HTTP listener has not failed again since the wrapper went in. The wrapper will not save me if the listener wedges the way it wedged last Thursday. Nothing will save me from the wedge except the cable. The wrapper saves me from the part I caused.

The other rule is older. The query string I sent was from a script I had not run in nine months. The script was for a different box. The box is in a closet. The Marantz is in the living room. I had typed the query string into a new script because it looked right. Looking right is the cheapest possible check, and the cheapest possible check is the one that costs five minutes the next time something else goes wrong.

The Marantz is back on the rack. The bad query string is in the file’s git history. The wrapper is the part of the work I can show. The cable is the part I cannot.


It’s October 19th, 2026, a Monday. The receiver is back on the rack. The wrapper is in the file. The cable is in the wall. The five minutes are over, and the next time they happen, the script will not be why.