Phorge at ed9d212013cd0d8747f92bca4e7cb94900bccfd5. PHP version irrelevant as we've also seen this in downstream for a while.
When editing a task via the "Add Action..." dropdown and e.g. changing its priority and status (adding a text comment is not needed),
- In PhabricatorEditEngine::buildResponse(), $action = 'comment' so the code does return $this->buildCommentResponse($object).
- PhabricatorEditEngine::buildCommentResponse($object) calls/checks if (!$request->isFormOrHisecPost()) { return new Aphront400Response(); }.
- AphrontRequest defines const TYPE_FORM = '__form__'.
- AphrontRequest::isFormOrHisecPost() checks that $post = $this->getExists(self::TYPE_FORM) && $this->isHTTPPost(). The first condition fails as $this->getExists(self::TYPE_FORM) returns false because the payload (which can be inspected via json_encode($request->getRequestData())) in this single call does NOT include "__form__":"1": '{"__preview__":"1","editengine.actions":"[{\"type\":\"priority\",\"value\":\"high\",\"initialValue\":null},{\"type\":\"status\",\"value\":\"invalid\",\"initialValue\":null}]","viewData":"[]","__ajax__":"true","__metablock__":"14","__path__":"\/maniphest\/task\/edit\/3801\/comment\/"}'
- As AphrontRequest::isFormOrHisecPost() returns false to PhabricatorEditEngine::buildCommentResponse(), it triggers return new Aphront400Response().
Thus I believe that the logic in rP543f2b6bf156be8eed7764927663d4366a8e1561 is slightly wrong.