MDB_dbx         *mt_dbxs;
        /** Array of MDB_db records for each known DB */
        MDB_db          *mt_dbs;
+       /** Array of sequence numbers for each DB handle */
+       unsigned int    *mt_dbiseqs;
 /** @defgroup mt_dbflag        Transaction DB Flags
  *     @ingroup internal
  * @{
        pgno_t          me_maxpg;               /**< me_mapsize / me_psize */
        MDB_dbx         *me_dbxs;               /**< array of static DB info */
        uint16_t        *me_dbflags;    /**< array of flags from MDB_db.md_flags */
+       unsigned int    *me_dbiseqs;    /**< array of dbi sequence numbers */
        pthread_key_t   me_txkey;       /**< thread-key for readers */
        MDB_pgstate     me_pgstate;             /**< state of old pages from freeDB */
 #      define          me_pglast       me_pgstate.mf_pglast
 #define TXN_DBI_EXIST(txn, dbi) \
        ((txn) && (dbi) < (txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & DB_VALID))
 
+       /** Check for misused \b dbi handles */
+#define TXN_DBI_CHANGED(txn, md_name, dbi) \
+       (!(md_name).mv_size || (txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi])
+
 static int  mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp);
 static int  mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp);
 static int  mdb_page_touch(MDB_cursor *mc);
        "MDB_BAD_RSLOT: Invalid reuse of reader locktable slot",
        "MDB_BAD_TXN: Transaction cannot recover - it must be aborted",
        "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size",
+       "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
 };
 
 char *
        /* Setup db info */
        txn->mt_numdbs = env->me_numdbs;
        txn->mt_dbxs = env->me_dbxs;    /* mostly static anyway */
+       memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int));
 
        if (txn->mt_flags & MDB_TXN_RDONLY) {
                if (!ti) {
                }
                tsize = sizeof(MDB_ntxn);
        }
-       size = tsize + env->me_maxdbs * (sizeof(MDB_db)+1);
+       size = tsize + env->me_maxdbs * (sizeof(MDB_db)+sizeof(unsigned int)+1);
        if (!(flags & MDB_RDONLY))
                size += env->me_maxdbs * sizeof(MDB_cursor *);
 
                return ENOMEM;
        }
        txn->mt_dbs = (MDB_db *) ((char *)txn + tsize);
+       txn->mt_dbiseqs = (unsigned int *) (txn->mt_dbs + env->me_maxdbs);
        if (flags & MDB_RDONLY) {
                txn->mt_flags |= MDB_TXN_RDONLY;
-               txn->mt_dbflags = (unsigned char *)(txn->mt_dbs + env->me_maxdbs);
+               txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs);
        } else {
-               txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
+               txn->mt_cursors = (MDB_cursor **)(txn->mt_dbiseqs + env->me_maxdbs);
                txn->mt_dbflags = (unsigned char *)(txn->mt_cursors + env->me_maxdbs);
        }
        txn->mt_env = env;
                                env->me_dbxs[i].md_name.mv_data = NULL;
                                env->me_dbxs[i].md_name.mv_size = 0;
                                env->me_dbflags[i] = 0;
+                               env->me_dbiseqs[i]++;
                                free(ptr);
                        }
                }
                mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
                for (i = 2; i < txn->mt_numdbs; i++) {
                        if (txn->mt_dbflags[i] & DB_DIRTY) {
+                               if (TXN_DBI_CHANGED(txn, txn->mt_dbxs[i].md_name, i)) {
+                                       rc = MDB_BAD_DBI;
+                                       goto fail;
+                               }
                                data.mv_data = &txn->mt_dbs[i];
                                rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, 0);
                                if (rc)
        env->me_path = strdup(path);
        env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
        env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
-       if (!(env->me_dbxs && env->me_path && env->me_dbflags)) {
+       env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int));
+       if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) {
                rc = ENOMEM;
                goto leave;
        }
                free(env->me_dbxs[i].md_name.mv_data);
 
        free(env->me_pbuf);
+       free(env->me_dbiseqs);
        free(env->me_dbflags);
        free(env->me_dbxs);
        free(env->me_path);
                /* Make sure we're using an up-to-date root */
                if (*mc->mc_dbflag & DB_STALE) {
                                MDB_cursor mc2;
+                               if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbx->md_name, mc->mc_dbi))
+                                       return MDB_BAD_DBI;
                                mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL);
                                rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0);
                                if (rc)
        if (mc->mc_dbi > MAIN_DBI && !(*mc->mc_dbflag & DB_DIRTY)) {
                MDB_cursor mc2;
                MDB_xcursor mcx;
+               if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbx->md_name, mc->mc_dbi))
+                       return MDB_BAD_DBI;
                mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx);
                rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
                if (rc)
                txn->mt_dbxs[slot].md_name.mv_size = len;
                txn->mt_dbxs[slot].md_rel = NULL;
                txn->mt_dbflags[slot] = dbflag;
+               txn->mt_dbiseqs[slot] = ++txn->mt_env->me_dbiseqs[slot];
                memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
                *dbi = slot;
                mdb_default_cmp(txn, slot);
        env->me_dbxs[dbi].md_name.mv_data = NULL;
        env->me_dbxs[dbi].md_name.mv_size = 0;
        env->me_dbflags[dbi] = 0;
+       env->me_dbiseqs[dbi]++;
        free(ptr);
 }
 
        if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
                return EACCES;
 
+       if (dbi > MAIN_DBI && TXN_DBI_CHANGED(txn, txn->mt_dbxs[dbi].md_name, dbi))
+               return MDB_BAD_DBI;
+
        rc = mdb_cursor_open(txn, dbi, &mc);
        if (rc)
                return rc;