From my understanding these event handlers serve different purposes:
a) OnError: This error handler handles errors that occur across the package and will be raised for each error that occurs.
b) OnTaskFailed: This error handler is raised when a specific task fails completely.
There are different ways to get this information back; but you could log errors to a table; consolidate and send a mail after that process for each task failure (so that you don't receive multiple emails) or you could receive the details for each error and then an informational notification of which task was affected. It all depends on what you're looking for.
This would be something I would work with for error information (fairly comprehensive) within a send mail task in the OnError event handler.
"This package task " + @[System::SourceName] + " ran at " + (DT_WSTR,50) @[System::StartTime] + ", and ended with the following error message for task: " + @[System::TaskName] + ". The error information is as follows: " + (DT_WSTR,50) @[System::ErrorCode] + " " + @[System::ErrorDescription]
Best way to see the purpose of each is to setup event handlers for both (with working variables i.e. as you have seen don't use @[System::ErrorCode] under the OnTaskFailed send mail task). Setup a task that will fail and look at the feedback you get via mail.
Hope this helps :-)
MCITP Sql Server 2008: Business Intelligence. 4 Years +