For errors raised by your own application, a common solution it to have a table of error messages like this:
create table errors
( error_no integer primary key
, error_text varchar2(200)
, error_cause varchar2(4000)
, error_action varchar2(4000)
);
A typical entry might be:
insert into errors (error_no, error_text, error_cause, error_action)
values (479, 'End date cannot be earlier than start date',
'A start date and an end date were entered where the end date was before the start date, which is not allowed.',
'Correct the start and end dates and retry.'
);
Then in your code handle exceptions something like this:
if p_start_date > p_end_date then
error_pkg.raise_error (479);
end if;
The package would do something like:
procedure raise_error (p_error_no integer)
is
l_text errors.error_text%type;
begin
select error_text into l_text
from errors
where error_no = p_error_no;
raise_application_error(-20001, l_text);
end;
The end user would see something like:
ERROR 479: End date cannot be earlier than start date
This could then be looked up to get the cause and action details.
A more advanced version would allow for data values to be displayed in the messages, using placeholders in the error text like this:
insert into errors (error_no, error_text, error_cause, error_action)
values (456, 'Invalid action code: [1]',
'An invalid action was specified', 'Correct the action code and retry.'
);
error_pkg.raise_error (456, p_act_code);