Migration bringing in duplicate taxonomy terms
I ended up biting the bullet and writing it myself.  I still don't know what was causing it to not work in the first place, but I replicated the desired behavior with this helper method in the migration class:

public function check_for_existing_term($taxonomy, $term) {
    $query = db_select('taxonomy_vocabulary', 'v')
        -&gt;condition('machine_name', $taxonomy);
    $query-&gt;innerJoin('taxonomy_term_data', 'td', 'v.vid=td.vid');
    $new_tid = $query-&gt;fields('td', array('tid'))
        -&gt;condition('td.name', $term)
        -&gt;execute()
        -&gt;fetchField();
    return ($new_tid) ? $new_tid : False;
}


In the constructor of the migration class I set $this-&gt;taxonomy = 'keywords'; and I used that in $this-&gt;destination = new MigrateDestinationTerm($this-&gt;taxonomy);, then I added the following prepareRow:

    public function prepareRow($row)
    {
        $new_tid = $this-&gt;check_for_existing_term($this-&gt;taxonomy, $row-&gt;NAME);
        if ($new_tid) {
            $this-&gt;map-&gt;saveIDMapping($row, array($new_tid),
                MigrateMap::STATUS_IGNORED, MigrateMap::ROLLBACK_DELETE);
            $this-&gt;rollbackAction = MigrateMap::ROLLBACK_DELETE;
            return FALSE;
        } else {
            return TRUE;
        }
    }


This tries to find a matching term in the database for each row.  If it finds it, it adds an entry in the map table and then returns FALSE so the migration won't do anything further for that row.  If it is a new term it returns True, and the migration continue happily, adding in the new term.