Quick Answer
Set DBACCESSOR_ROWDATA for row accessors, DBACCESSOR_PARAMETERDATA for parameter accessors — and make sure the dwAccessorFlags you pass to IAccessor::CreateAccessor actually match the bindings you're using.
Why This Happens
I know this error is infuriating — you're just trying to read some data and the provider throws a tantrum about flags. I've tripped over this one myself back in my help desk days. The root cause is usually a simple mismatch: your code tells OLE DB one thing about the accessor, but the binding structure says something else.
Every OLE DB accessor has a flag that tells the provider what it's for. DBACCESSOR_ROWDATA (0x1) means it's for fetching rows. DBACCESSOR_PARAMETERDATA (0x2) means it's for parameters. DBACCESSOR_COLUMN (0x4) is a newer one for column-level access — but not every provider supports that. When you create an accessor, you pass those flags. If they don't match what the bindings in your DBBINDING array expect, you get ERROR_BAD_ACCESSOR_FLAGS.
Here's the sneaky part: a lot of beginner code uses the same accessor for both row data and parameters. That's a no-no. The provider needs to know the difference because it interprets the bindings completely differently. I've seen this happen with SQL Server Native Client 11.0 and older MSDASQL drivers — they're strict about it.
A real-world trigger: you're building a parameterized query. You create an accessor for the parameters, pass DBACCESSOR_PARAMETERDATA, but your DBBINDING array has iOrdinal values pointing to row columns instead of parameter ordinals. The provider throws 0x305 right in your face.
Fix Steps
- Check your accessor flags — Open the code where you call
IAccessor::CreateAccessor. Look at the third argument,dwAccessorFlags. If you're binding row data, it should beDBACCESSOR_ROWDATA. If it's for parameters, useDBACCESSOR_PARAMETERDATA. Never mix them in one call. - Verify the binding type — In your
DBBINDINGstructure, check theeParamIOfield. For parameters, it must beDBPARAMIO_INPUT,DBPARAMIO_OUTPUT, or both. For row columns, it should be 0. If you're seeing 0x305, there's a good chance this field is wrong. - Check the ordinal values — For parameter accessors,
iOrdinalstarts at 1 for the first parameter, 2 for the second, and so on. For row accessors, it's the column number. If you accidentally pass a row column number to a parameter binding, the provider will scream. - Update your OLE DB driver — If the code looks correct, you might be dealing with a driver bug. The old Jet OLE DB providers (Microsoft.Jet.OLEDB.4.0) are notorious for this. Switch to the newer ACE provider (Microsoft.ACE.OLEDB.12.0 or 16.0) if you're working with Access files. For SQL Server, use Microsoft OLE DB Driver for SQL Server (MSOLEDBSQL) instead of the legacy SQLNCLI.
- Test with a minimal repro — Strip your code down to just one binding and one accessor. If that works, add them back one by one until you find the culprit. I've solved more bugs this way than any debugger.
Alternative Fixes
If the steps above don't work, try these:
- Check for bitwise OR mistakes — Some developers do
DBACCESSOR_ROWDATA | DBACCESSOR_PARAMETERDATAthinking they're being clever. That's a one-way ticket to 0x305. Use only one flag per accessor. - Look at the column count — Your
DBBINDINGarray must match the number of columns or parameters you're binding. If you bound 5 but pass 3, the provider might misinterpret the flags. - Rebuild after changing the schema — If you updated a table or a stored procedure, the metadata might have changed. Re-run
IColumnsInfo::GetColumnInfoand regenerate your bindings.
Prevention Tip
Always build your accessor flags based on the actual use case, not by copy-pasting code from another project. Add a comment next to each CreateAccessor call explaining which flag you're using and why. And test your code against the newest OLE DB driver — old ones have quirks that'll bite you in production.
You'll nail this in ten minutes now that you know where to look. Trust me, it's almost always the flags.