Index: src/ControlStruct/ExceptDecl.cpp
===================================================================
--- src/ControlStruct/ExceptDecl.cpp	(revision 10b5970650e2ff3d8e757dd5c91889d466e44fe0)
+++ src/ControlStruct/ExceptDecl.cpp	(revision 190a833c2baaf0fb88c72cb00720c5ab476197bc)
@@ -445,4 +445,11 @@
 }
 
+ast::NameExpr const * designatedName( ast::Designation const * des ) {
+	if ( des && 1 == des->designators.size() ) {
+		return des->designators.front().as<ast::NameExpr>();
+	}
+	return nullptr;
+}
+
 ast::ObjectDecl const * ExceptDeclCore::transformVTable(
 		ast::ObjectDecl const * decl, ast::VTableType const * type ) {
@@ -467,10 +474,42 @@
 				createTypeIdValue( location, exceptionName, params ) );
 		}
-		declsToAddBefore.push_back(
-			createCopy( location, exceptionName, params ) );
-		declsToAddBefore.push_back(
-			createMsg( location, exceptionName, params ) );
 		retDecl = createVirtualTable(
 			location, exceptionName, params, tableName );
+		// There is quite a bit of work to pull over any initializers and
+		// decide if we want to insert the default functions.
+		bool foundCopy = false;
+		bool foundMsg = false;
+		ast::ListInit const * init = decl->init.as<ast::ListInit>();
+		if ( init ) {
+			for ( size_t i = 0 ; i < init->initializers.size() ; ++i ) {
+				ast::Designation const * des = init->designations.at(i);
+				auto name = designatedName( des );
+				if ( nullptr == name ) continue;
+				if ( "copy" == name->name ) {
+					foundCopy = true;
+				} else if ( "msg" == name->name ) {
+					foundMsg = true;
+				}
+				auto retInit = retDecl->init.as<ast::ListInit>();
+				for ( size_t j = 0 ; j < retInit->initializers.size() ; ++j ) {
+					ast::Designation const * des = retInit->designations.at(j);
+					auto retName = designatedName( des );
+					if ( retName && name->name == retName->name ) {
+						retInit = ast::mutate_field_index( retInit,
+							&ast::ListInit::initializers, j,
+							init->initializers.at(i) );
+					}
+				}
+				retDecl->init = retInit;
+			}
+		}
+		if ( !foundCopy ) {
+			declsToAddBefore.push_back(
+				createCopy( location, exceptionName, params ) );
+		}
+		if ( !foundMsg ) {
+			declsToAddBefore.push_back(
+				createMsg( location, exceptionName, params ) );
+		}
 	}
 
Index: tests/exceptions/.expect/message.txt
===================================================================
--- tests/exceptions/.expect/message.txt	(revision 190a833c2baaf0fb88c72cb00720c5ab476197bc)
+++ tests/exceptions/.expect/message.txt	(revision 190a833c2baaf0fb88c72cb00720c5ab476197bc)
@@ -0,0 +1,2 @@
+Out of Range Index 10 (expected less than 8)
+5
Index: tests/exceptions/message.cfa
===================================================================
--- tests/exceptions/message.cfa	(revision 190a833c2baaf0fb88c72cb00720c5ab476197bc)
+++ tests/exceptions/message.cfa	(revision 190a833c2baaf0fb88c72cb00720c5ab476197bc)
@@ -0,0 +1,71 @@
+// Use of the exception system where we provide our own functions.
+
+#include <stdio.h>
+#include <string.h>
+// Using collections/string.hfa leads to a resolution error on snprintf.
+
+exception BadIndexException {
+	int value;
+	int size;
+	char * message;
+};
+
+const char * virtual_msg(BadIndexException * this) {
+	return this->virtual_table->msg(this);
+}
+
+// Array length calculated to ususually be big enough.
+const size_t msg_size = 52;
+
+char * format_bad_index(int value, int size) {
+	char * out = (char *)(void *)malloc(msg_size + 1);
+	snprintf(out, msg_size,
+		"Out of Range Index %d (expected less than %d)", value, size);
+	return out;
+}
+
+const char * msg(BadIndexException * this) {
+	if (this->message) {
+		free(this->message);
+	}
+	this->message = format_bad_index(this->value, this->size);
+	return this->message;
+}
+
+void copy(BadIndexException * dst, BadIndexException * src) {
+	// This is an easy detail to miss, you have to copy the table over.
+	dst->virtual_table = src->virtual_table;
+
+	dst->value = src->value;
+	dst->size = src->size;
+	dst->message = (src->message) ? strndup(src->message, msg_size) : 0p;
+}
+
+void ^?{}(BadIndexException & this) {
+	free(this.message);
+}
+
+vtable(BadIndexException) arrayIndex = {
+	.msg = msg,
+	.copy = copy,
+	.^?{} = ^?{},
+};
+
+// This is not supposed to be a real range check, but that's the idea:
+void failRangeCheck(int index, int size) {
+	throw (BadIndexException){ &arrayIndex, index, size };
+}
+
+int atDefault(int fallback) {
+	try {
+		failRangeCheck(10, 8);
+	} catch (BadIndexException * error) {
+		printf("%s\n", virtual_msg(error));
+	}
+	return fallback;
+}
+
+int main() {
+	int value = atDefault(5);
+	printf("%d\n", value);
+}
