The difference has to do with the upper bound on the runtime. When considering a polynomial-time algorithm, if you have a polynomial-time acceptor, you can turn it into a polynomial-time decider as follows:
- Run the algorithm for the polynomial amount of time it would take to accept in the worst-case.
- If it accepts in this time, great! Accept.
- If it doesn't accept in this time, it's never going to accept. Reject.
Therefore, the accepter can be turned into a decider.
Now, think about the same thing for the halting problem:
- Run the algorithm as long as it would take to accept in the worst-case.
- If it accepts in this time, great! Accept.
- If it doesn't accept in this time, it's never going to accept. Reject.
The problem here is that there isn't some fixed amount of time in which the algorithm will accept - programs can run arbitrarily long before accepting, so there's no way to say "run it until it would have already accepted," since there's no computational process that can figure out what time this is.
Interestingly, this connects to the Busy Beaver function. A busy-beaver of size n is, intuitively, a program of length n that always halts, but takes the longest possible time to halt out of all programs of size n. For a particular input w, the busy beaver number for w of order n is the number of steps it will take the busy beaver program of size n to halt on the input w. This number is mathematically well-defined, but it can't be computed by any computer program or otherwise you could use it to make the above algorithm work correctly.
Hope this helps!