         f = (mesg) =>
             if mesg.parent_header.msg_id == message.header.msg_id
                 dbg("got message -- #{JSON.stringify(mesg)}")
+                done = mesg.content?.execution_state == 'idle'  # check this before giving opts.cb the chance to mutate.
+
+                # TODO: mesg isn't a normal javascript object; it's **silently** immutable, which
+                # is pretty annoying for our use. For now, we just copy it, which is a waste.
                 mesg = misc.copy_with(mesg,['metadata', 'content', 'buffers'])
-                # TODO: mesg isn't a normal javascript object; it's **silently** immutable, which
-                # is pretty annoying for our use. Investigate.  For now, we just copy it, which is a waste.
                 mesg = misc.deep_copy(mesg)
                 if opts.all
                     all_mesgs.push(mesg)
                 else
                     opts.cb(undefined, mesg)
-                if mesg.content?.execution_state == 'idle'
+                if done
                     @removeListener('iopub', f)
+                    @_execute_code_queue.shift()   # finished
+                    @_process_execute_code_queue()
                     if opts.all
                         opts.cb(undefined, all_mesgs)
             else

            fs.unlink(@_kernel.connectionFile)
            delete @_kernel
            delete @_channels

    dbg: (f) =>
        if not @_dbg?

            code : required
            all  : false       # if all=true, cb(undefined, [all output messages]); used for testing mainly.
            cb   : required    # if all=false, this happens **repeatedly**:  cb(undefined, output message)

        @_execute_code_queue ?= []
        @_execute_code_queue.push(opts)
        if @_execute_code_queue.length == 1
            @_process_execute_code_queue()

    _process_execute_code_queue: =>
        dbg = @dbg("_process_execute_code_queue")
        if not @_execute_code_queue?
            dbg("no queue")
            return
        n = @_execute_code_queue.length
        if n == 0
            dbg("queue is empty")
            return
        dbg("queue has #{n} items; ensure kernel running")
        @_ensure_running (err) =>
            if err
                dbg("error running kernel -- #{err}")
                opts.cb(err)
            else
                dbg("now executing oldest item in queue")
                @_execute_code(@_execute_code_queue[0])
        return

    _clear_execute_code_queue: =>
        # ensure no future queued up evaluation occurs (currently running
        # one will complete and new executions could happen)
        if not @_execute_code_queue?
            return
        for opts in @_execute_code_queue.slice(1)
            opts.cb('interrupt')
        @_execute_code_queue = []

    _execute_code: (opts) =>
        opts = defaults opts,
