Looks like it's an infinite loop. When the promise resolves, it re-runs the filters, and when the filter's promise resolves it re-runs them again. This seems to happen any time a filter returns a promise. Pretty silly. I think I can break out of this behavior by memoizing my promises externally, but I thought promises were supposed to self-memoize so angular must be breaking something here.
If I pass a promise into a filter, the value the filter sees is 'null'. Angular re-runs the filter a few times with nulls, and then eventually with the actual data. I have to guard my filters against null inputs because of this silliness, or they'll end up spouting lots of errors.
So, that's the workaround I'm using for now. Guard all my filters with if statements until their inputs aren't null, and then memoize the result to prevent an infinite loop. It would be nice if I could just use promises in my filters though.