From a quick check I just did on Account/Contact, it looks like the getSObjects
method returns null when there are no child values instead of returning an empty list. (Not helpful or documented and a bit of a surprise.) Test code:
for (Account a : [select Name, (select Name from Contacts) from Account limit 3]) {
System.debug('>>> d ' + a.getSObjects('Contacts'));
System.debug('>>> s ' + a.Contacts);
}
outputs:
06:40:44.11 (18453282)|USER_DEBUG|[2]|DEBUG|>>> d null
06:40:44.11 (18616912)|USER_DEBUG|[3]|DEBUG|>>> s ()
06:40:44.11 (18781939)|USER_DEBUG|[2]|DEBUG|>>> d null
06:40:44.11 (18851803)|USER_DEBUG|[3]|DEBUG|>>> s ()
06:40:44.11 (19062520)|USER_DEBUG|[2]|DEBUG|>>> d (Contact:{AccountId=0015000000curPjAAI, Id=0035000001mZBEcAAO, Name=Test Person})
06:40:44.11 (19166666)|USER_DEBUG|[3]|DEBUG|>>> s (Contact:{AccountId=0015000000curPjAAI, Id=0035000001mZBEcAAO, Name=Test Person})
You could add an extra check for that null value, but if you use the "static" approach to accessing the list that problem doesn't occur and you get the expected empty list.
So change the line to:
List<Vehicle__c> vList = t.Vehicles__r;
The error message for the current code should be "you cannot edit"; as Shankish points out you need to handle "before delete" to deal with the delete case.
PS
In response to your comment, the error has to be added to the row instance that is in the trigger context so:
Trigger.newMap.get(t.Id).addError('you cannot edit');