Bitcoin's consensus rules are defined by (economically relevant) running nodes. Right now, those are primarily various versions of Bitcoin Core, but there is no inherent reason why that would always be the case.
What this means is that there cannot be a specification of those rules. Imagine we write a document with as close a description of what the most recent version of Bitcoin Core does as we can muster, and declare it to be "The Bitcoin protocol". Now what happens if we discover that the code actually does something slightly different. Which is now wrong? The code or the document? If it's something that constitutes a serious problem (say, stealing money), people may try to convince the world to migrate to a modified implementation, or a different one. But this approach cannot be guaranteed: node owners are sovereign and nobody can force them to adopt new code. In the end, it would just mean that our document is not a correct description of the rules.
Ultimately, this means that at best we can create a document that describes our best knowledge of the consensus rules, but not one that prescribes it. The code that people actually run has the last word, always.
So why can't we just make an exact description of what the current code does? Because we don't know. Knowing the exact behavior of code is remarkably difficult. In what follows I'm going to use the term "quirk" rather than "bug", because for all intents and purposes, these details constitute the Bitcoin protocol, for better or for worse. Here are some examples:
Initially Bitcoin relied on OpenSSL for ECDSA verification. ECDSA is well specified, and one would expect that what OpenSSL implements is exactly follows that specification. It didn't. It allowed various weird deviations from the DER standard (which specifies how the big numbers in ECDSA signatures are turned into bytes). This doesn't break the security of the signature system (it's still impossible for someone without private key to produce a signature), but it did pose a problem for Bitcoin as it meant we'd have to maintain OpenSSL's quirks forever. This was eventually resolved using a softfork (BIP 66) that restricted the set of valid signatures to a well-defined subset. In fact, the situation was even worse than this: OpenSSL turned out to be inconsistent with itself about exactly which deviations from DER were accepted: it behaved different on 64-bit Linux and Windows, which would have allowed an attacker to fork the network between those two. You can read my disclosure of this issue (after BIP 66 solved it) here.
In March 2013, the network experienced a fork between nodes running Bitcoin 0.7 and those running 0.8. The history is described as a port-mortem in BIP 50. The problem was that every version before 0.8 used a database library called BDB. BDB requires configuring a maximum number of "locks" (limiting the number of simultaneous operations that could be performed). It turned out that this number was by default slightly too low. Bitcoin 0.8 replaced BDB with LevelDB which did not have any such requirement. 0.8 was significantly faster, so miners upgraded quickly - and accidentally constructed a block that exceeded the maximum number of locks 0.7 used. The majority of the ecosystem other than miners was still on 0.7, and they rejected the majority chain. It was resolved by having miners revert to 0.7 temporarily, introducing a temporary softfork that "simulated" the locks limit in 0.8, and eventually having the entire network on 0.8.
As you can see, these things aren't as easy as looking at a function and thinking about what it does. These examples both involved an external dependency out of Bitcoin's control, and not quite exactly specified behavior for those. That doesn't mean they're the only source of quirks, but they certainly complicate things.
For example the same function written in C++ will not work exactly the same as one in Python.
I hope you see now this has actually nothing to do with programming language. If the exact consensus rules were known it would certainly be possible to replicate them in another language (assuming it were possible to also reason exactly about the behavior in that other language). Conversely, reimplementing the consensus in the same programming language has the same problems. The point is simply not taking unnecessary risks when dealing with consensus code - using the code that's already in use has the lowest risk, and small easily auditable changes to it where necessary for performance reasons or intentional protocol changes.
The problem also isn't the known quirks - it's the unknown ones, and I cannot tell you where those are. There are plenty of gotchas that have been discovered over the years and are still relevant in the current rules - and maybe that's the only thing you were after in your question. In that case, there is an old answer here: Satoshi's bitcoind implementation bugs. I'll try to think if I can come up with more recent examples to add there.