diff --git a/IPython/frontend/html/notebook/static/js/kernel.js b/IPython/frontend/html/notebook/static/js/kernel.js
index ea80e9e..e446d5c 100644
--- a/IPython/frontend/html/notebook/static/js/kernel.js
+++ b/IPython/frontend/html/notebook/static/js/kernel.js
@@ -351,7 +351,7 @@ var IPython = (function (IPython) {
         var reply = $.parseJSON(e.data);
         var content = reply.content;
         var msg_type = reply.header.msg_type;
-        var metadata = reply.metadata || {};
+        var metadata = reply.metadata;
         var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
         if (msg_type !== 'status' && callbacks === undefined) {
             // Message not from one of this notebook's cells and there are no
diff --git a/IPython/zmq/session.py b/IPython/zmq/session.py
index 6c16c63..b5a7ee6 100644
--- a/IPython/zmq/session.py
+++ b/IPython/zmq/session.py
@@ -421,11 +421,9 @@ class Session(Configurable):
         msg['msg_type'] = header['msg_type']
         msg['parent_header'] = {} if parent is None else extract_header(parent)
         msg['content'] = {} if content is None else content
-        metadata_dict = self.metadata.copy()
+        msg['metadata'] = self.metadata.copy()
         if metadata is not None:
-            metadata_dict.update(metadata)
-        if metadata_dict:
-            msg['metadata'] = metadata_dict
+            msg['metadata'].update(metadata)
         return msg
 
     def sign(self, msg_list):
@@ -459,7 +457,7 @@ class Session(Configurable):
         -------
         msg_list : list
             The list of bytes objects to be sent with the format:
-            [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content,
+            [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_metadata,p_content,
              buffer1,buffer2,...]. In this list, the p_* entities are
             the packed or serialized versions, so if JSON is used, these
             are utf8 encoded JSON strings.
@@ -480,7 +478,8 @@ class Session(Configurable):
 
         real_message = [self.pack(msg['header']),
                         self.pack(msg['parent_header']),
-                        content
+                        self.pack(msg['metadata']),
+                        content,
         ]
 
         to_send = []
@@ -607,7 +606,7 @@ class Session(Configurable):
             The ZMQ stream or socket to use for sending the message.
         msg_list : list
             The serialized list of messages to send. This only includes the
-            [p_header,p_parent,p_content,buffer1,buffer2,...] portion of
+            [p_header,p_parent,p_metadata,p_content,buffer1,buffer2,...] portion of
             the message.
         ident : ident or list
             A single ident or a list of idents to use in sending.
@@ -705,7 +704,7 @@ class Session(Configurable):
         -----------
         msg_list : list of bytes or Message objects
             The list of message parts of the form [HMAC,p_header,p_parent,
-            p_content,buffer1,buffer2,...].
+            p_metadata,p_content,buffer1,buffer2,...].
         content : bool (True)
             Whether to unpack the content dict (True), or leave it packed
             (False).
@@ -719,7 +718,7 @@ class Session(Configurable):
             The nested message dict with top-level keys [header, parent_header,
             content, buffers].
         """
-        minlen = 4
+        minlen = 5
         message = {}
         if not copy:
             for i in range(minlen):
@@ -741,12 +740,13 @@ class Session(Configurable):
         message['msg_id'] = header['msg_id']
         message['msg_type'] = header['msg_type']
         message['parent_header'] = self.unpack(msg_list[2])
+        message['metadata'] = self.unpack(msg_list[3])
         if content:
-            message['content'] = self.unpack(msg_list[3])
+            message['content'] = self.unpack(msg_list[4])
         else:
-            message['content'] = msg_list[3]
+            message['content'] = msg_list[4]
 
-        message['buffers'] = msg_list[4:]
+        message['buffers'] = msg_list[5:]
         return message
 
 def test_msg2obj():
diff --git a/IPython/zmq/tests/test_session.py b/IPython/zmq/tests/test_session.py
index 5a440e6..accc179 100644
--- a/IPython/zmq/tests/test_session.py
+++ b/IPython/zmq/tests/test_session.py
@@ -47,10 +47,11 @@ class TestSession(SessionTestCase):
     def test_msg(self):
         """message format"""
         msg = self.session.msg('execute')
-        thekeys = set('header parent_header content msg_type msg_id'.split())
+        thekeys = set('header parent_header metadata content msg_type msg_id'.split())
         s = set(msg.keys())
         self.assertEqual(s, thekeys)
         self.assertTrue(isinstance(msg['content'],dict))
+        self.assertTrue(isinstance(msg['metadata'],dict))
         self.assertTrue(isinstance(msg['header'],dict))
         self.assertTrue(isinstance(msg['parent_header'],dict))
         self.assertTrue(isinstance(msg['msg_id'],str))
@@ -69,6 +70,7 @@ class TestSession(SessionTestCase):
         self.assertEqual(new_msg['header'],msg['header'])
         self.assertEqual(new_msg['content'],msg['content'])
         self.assertEqual(new_msg['parent_header'],msg['parent_header'])
+        self.assertEqual(new_msg['metadata'],msg['metadata'])
         # ensure floats don't come out as Decimal:
         self.assertEqual(type(new_msg['content']['b']),type(new_msg['content']['b']))
 
@@ -85,6 +87,7 @@ class TestSession(SessionTestCase):
         self.assertEqual(new_msg['header'],msg['header'])
         self.assertEqual(new_msg['content'],msg['content'])
         self.assertEqual(new_msg['parent_header'],msg['parent_header'])
+        self.assertEqual(new_msg['metadata'],msg['metadata'])
         self.assertEqual(new_msg['buffers'],[b'bar'])
 
         socket.data = []
@@ -92,9 +95,10 @@ class TestSession(SessionTestCase):
         content = msg['content']
         header = msg['header']
         parent = msg['parent_header']
+        metadata = msg['metadata']
         msg_type = header['msg_type']
         self.session.send(socket, None, content=content, parent=parent,
-            header=header, ident=b'foo', buffers=[b'bar'])
+            header=header, metadata=metadata, ident=b'foo', buffers=[b'bar'])
         ident, msg_list = self.session.feed_identities(socket.data)
         new_msg = self.session.unserialize(msg_list)
         self.assertEqual(ident[0], b'foo')
@@ -102,6 +106,7 @@ class TestSession(SessionTestCase):
         self.assertEqual(new_msg['msg_type'],msg['msg_type'])
         self.assertEqual(new_msg['header'],msg['header'])
         self.assertEqual(new_msg['content'],msg['content'])
+        self.assertEqual(new_msg['metadata'],msg['metadata'])
         self.assertEqual(new_msg['parent_header'],msg['parent_header'])
         self.assertEqual(new_msg['buffers'],[b'bar'])
 
@@ -114,6 +119,7 @@ class TestSession(SessionTestCase):
         self.assertEqual(new_msg['msg_type'],msg['msg_type'])
         self.assertEqual(new_msg['header'],msg['header'])
         self.assertEqual(new_msg['content'],msg['content'])
+        self.assertEqual(new_msg['metadata'],msg['metadata'])
         self.assertEqual(new_msg['parent_header'],msg['parent_header'])
         self.assertEqual(new_msg['buffers'],[b'bar'])
 
diff --git a/docs/source/development/messaging.txt b/docs/source/development/messaging.txt
index 822ad66..d723b89 100644
--- a/docs/source/development/messaging.txt
+++ b/docs/source/development/messaging.txt
@@ -106,7 +106,7 @@ A message is defined by the following four-dictionary structure::
       # depends on the message type.
       'content' : dict,
       
-      # Any metadata associated with the message; this dictionary is optional.
+      # Any metadata associated with the message.
       'metadata' : dict,
     }
 
@@ -130,7 +130,7 @@ messages upon deserialization to the following form for convenience::
       'msg_type' : str,
       'parent_header' : dict,
       'content' : dict,
-      'metadata' : dict, # optional
+      'metadata' : dict,
     }
 
 All messages sent to or received by any IPython process should have this