Serial Error Correction
Welcome to the wide world of Communications.  You are asking about "Error Detection and Correction".  This is different from "framing", which allows you to determine when a message starts and ends.

With "error detection", you know there was an error, but you can't necessarily recover from it.  With "correction", you know there was an error and you can determine what was originally sent.

This always requires extra bytes.  There are many schemes, but the Fletcher checksum for Error Detection is easy to implement, fast, and only requires 2 extra bytes in the message (4 if hex digits).

The generic approach is to use a finite-state machine.  In complicated message protocols, there would be nested state machines: the inner-most would probably determine framing, the next one out might looks at addressing, etc.  Any or all of those layers could use Error Detection and/or Correction.

For your simple "protocol", a single state machine is probably sufficient.  The states might be IDLE, RX_PAYLOAD and RX_CS.  As characters are received, they are processed according to the current state and occasionally change the state variable.

while (Serial.available()) {
  uint8_t c = Serial.read();
  switch (state) {
    case IDLE:
      if (c == MSG_START)
        state = RX_PAYLOAD;
      break;
    case RX_PAYLOAD:
      if (c == MSG_END) {
        state = RX_CS;
        cs = 0;
        csCount = 0;
      } else if (length &lt; MAX_LENGTH-1) {
        msg[ length++ ] = c;
      }
      break;
    case RX_CS:
      //  Add in the next hex CS digit
      cs = cs * 16 + fromHex(c);
      if (++csCount == 2) {
        // both digits received, see if the CS matches
        if (cs == csOfMsg( msg, length )) {
          // Handle the message
          msg[ length ] = '\0';
          Serial.println( msg );
        } else {
          Serial.println( F("Bad message!") );
        }
        state = IDLE;
      }
      break;
  } // end switch
} // end while


There are entire books written on protocols and their design.  This is just a made-up example to give you an idea of how it should be structured: a finite state machine, driven by received character "events".

Sometimes you can compute the checksum as the characters are received, like Fletcher.  Here, it waits until the entire message plus 2 CS bytes are received.