Turns out the build failure was because of a particular task failing but not outputting any errors:
Task "CollectFilesinFolder" (TaskId:470)
Done executing task "CollectFilesinFolder" -- FAILED. (TaskId:470)
I looked at this task in a decompiler and saw that it loads an error message but never actually does anything with the error message (because who would like to actually see the reason for a build failure?):
if (!Directory.Exists(this.RootPath))
{
SR.GetString("BUILDTASK_ColectFilesInFolder_RootIsNotValid", this.RootPath);
return false;
}