Your Access database converted cleanly — no obvious errors, tables intact, data all there. Then you open a form, click a button, and get hit with a VBA compile error. "Sub or Function not defined." "Compile error." The database that worked fine yesterday is suddenly broken.
This is one of the most common post-migration headaches, and it almost always has one of three causes: a broken VBA library reference, a name collision with a new built-in function, or a DAO/ADODB syntax change. All three are fixable without touching your business logic.
Open the VBA editor with Alt+F11, then go to Tools → References. Scan the list for any entry that starts with the word MISSING. That's your culprit.
When Access converts an .mdb to .accdb, it preserves the list of referenced libraries — but it can't update the registered paths. If you also upgraded Office (from 2010 to 365, for example), the paths to those libraries have changed. The reference still points to the old location, which no longer exists, so VBA can't compile anything that depends on it.
| Reference | Why it breaks | Fix |
|---|---|---|
| Microsoft DAO 3.6 Object Library | Registered path differs across Office versions | Uncheck MISSING, re-check the current entry |
| Microsoft Access 16.0 Object Library | Version number in path is literal; 14.0 path no longer valid on 365 | Uncheck the old version, check the current one |
| Microsoft Office 16.0 Object Library | Same version-number issue | Uncheck old, check current |
| Microsoft ActiveX Data Objects 2.x | ADODB version registered may differ from what's listed | Uncheck, re-check the matching installed version |
| Custom OCX / ActiveX controls | Control not registered on the machine, or .ocx file path changed | Re-register the OCX or remove/replace the control |
The fix is straightforward: uncheck the MISSING entry, scroll the list to find the current equivalent, check that, and click OK. Then go to Debug → Compile to verify the reference fix resolved the errors.
LegacyLeaps scans your .mdb file and flags broken or version-mismatched VBA references before migration — so you know what to expect. Try the free scan first.
Run the Free ScanDon't wait for errors to surface one by one at runtime. After fixing any missing references, use Debug → Compile [YourProjectName] in the VBA editor. This compiles all modules simultaneously and stops at the first error it finds.
Fix that error, compile again, and repeat until the compile runs clean. One broken reference can cause dozens of apparent errors across multiple modules — fixing the reference and recompiling often clears most of them in a single step.
Access 2007 and later added or promoted several functions to the VBA built-in library that didn't exist (or existed differently) in the older Jet-era Access. If your .mdb defined custom functions or subroutines with the same names, you now have a collision: VBA sees the name and doesn't know which version to call.
Functions that commonly collide with legacy custom names:
Split — very commonly defined as a custom string utility in old .mdb codeJoin — same categoryFilter — frequently used as a wrapper function nameReplace — often custom-implemented in Access 97/2000-era databasesInStrRev, StrReverse — added in later VBA versionsFormatCurrency, FormatNumber, FormatPercent — commonly wrappedMonthName, WeekdayName — date utilities that were DIY in older databasesThe fix is simple: rename your custom functions to avoid the collision. For example, rename your custom Split to SplitCustom or SplitDelimited, update all call sites, and the conflict disappears. The built-in version is better anyway — use it where you can and remove the custom implementation entirely.
Most DAO code that worked in .mdb works identically in .accdb. But there are a handful of patterns that behave differently under ACE (the .accdb engine).
In Jet (.mdb), you could reference open form controls directly in SQL passed to OpenRecordset:
' Works in .mdb, fails in .accdb
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT * FROM Orders WHERE CustomerID = Forms!Form1!txtCustomerID")
ACE evaluates the SQL in a different context and can't resolve the form reference. The fix: pull the value into a variable first, then build the SQL string:
Dim rs As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT * FROM Orders WHERE CustomerID = " & Forms!Form1!txtCustomerID
Set rs = CurrentDb.OpenRecordset(strSQL)
ACE has a slightly expanded list of reserved words compared to Jet. Column names like Date, Name, Level, Value, Description, and Status are reserved in ACE. If your tables use these as field names and your SQL doesn't bracket them, you'll get syntax errors in .accdb that didn't exist in .mdb.
Fix: wrap reserved field names in square brackets in all your queries and SQL strings:
' Before
"SELECT Date, Name FROM Projects"
' After
"SELECT [Date], [Name] FROM Projects"
If you're not sure whether a compile error is caused by a broken reference or by a logic problem in the code itself, switch to late binding temporarily as a test:
' Early binding (requires valid reference)
Dim rs As DAO.Recordset
Dim db As DAO.Database
' Late binding (no reference needed — for testing only)
Dim rs As Object
Dim db As Object
Set db = CurrentDb
If the late-bound version runs correctly, the problem is your reference, not your code logic. Fix the reference, switch back to early binding (it's faster and gives you IntelliSense), and you're done.
LegacyLeaps preserves your Access VBA, forms, and queries during migration — and flags the patterns most likely to cause compile errors so you can fix them before go-live.
Try the Free ScanMost .mdb-to-.accdb VBA errors are fixable in an afternoon. But if your database is large, has dozens of modules, and is showing compile errors across the board, that's a sign of deeper architectural debt. The VBA was written for a runtime environment that no longer exists, and patching it one error at a time is a temporary fix to a permanent problem.
At that point, the real question is whether your Access application should be rebuilt from the ground up on a modern stack — one that doesn't depend on a 30-year-old desktop database engine, handles multiple users properly, and doesn't break every time Microsoft updates Office.
AccessLift migrates your Access database — tables, forms, VBA logic, and reports — to a modern web application. No more compile errors. No more Access dependency. Your data stays on your machine during the migration.
Learn About AccessLiftWork through the list in order. In most migrations, steps 1-3 resolve 90% of the compile errors.
If you're still stuck after working through this checklist, the done-for-you service includes full VBA audit and repair — we fix the errors, you get a working database.
Practical fixes for legacy Excel and Access problems. No spam.