Repository navigation
google-cloud-ndb unsafe access of free variable 'lock' #17926
Description
Determine this is the right repository
- I determined this is the correct repository in which to report this bug.
Summary of the issue
Context
I was wrapping some previously implemented code into an NDB Transaction. When I executed the code, an exception was raised.
Expected Behavior:
I expected the previously successful code to commit the transaction successfully.
Actual Behavior:
After wrapping the code in @ndb.transactional() most of the changes to the database did get persisted, but an exception was raised from the transaction closing:
NameError: cannot access free variable 'lock' where it is not associated with a value in enclosing scope
at .callback ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_datastore_api.py:455 )
at ._transaction_async ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_transaction.py:312 )
at ._advance_tasklet ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/tasklets.py:323 )
at .retry_wrapper ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_retry.py:82 )
at .retry_wrapper ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_retry.py:97 )
at ._advance_tasklet ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/tasklets.py:319 )
at .check_success ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/tasklets.py:157 )
at .result ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/tasklets.py:210 )
at .transaction ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_transaction.py:186 )
at .transactional_inner_wrapper ( /layers/google.python.pip/pip/lib/python3.13/site-packages/google/cloud/ndb/_transaction.py:347 )
at .updateCompany ( /workspace/Api/App/ServiceLayer/Services/AdminPortal/AdminPortalCompanyRoutes.py:96 )
at .decorated_function ( /workspace/AppengineCommon/Lib/CommonApi/Auth/AuthorizationHelper.py:110 )
at .inner ( /layers/google.python.pip/pip/lib/python3.13/site-packages/flask_classful.py:271 )
at .proxy ( /layers/google.python.pip/pip/lib/python3.13/site-packages/flask_classful.py:303 )
I dug into this and found that we were performing an ndb.delete_multi(..., use_datastore=False, use_cache=True, use_global_cache=True) in order to directly influence only the state of our redis cache. This was some stupid legacy code that I've rectified, but I'm submitting this bugfix so that the code can be cleared up or so that others can find this issue in the future.
API client name and version
google-cloud-ndb==2.3.4
Reproduction steps: code
file: main.py
def reproduce():
# complete code hereReproduction steps: supporting files
Here is a simple reproduction that can cause this exception:
from google.cloud import ndb
if __name__ == "__main__":
class Thing(ndb.Model):
pass
key = Thing(id='id').put()
@ndb.transactional()
def doIt():
key.delete(use_datastore=False)
doIt()
Reproduction steps: actual results
file: output.txtmydata.csv
Calculated: foo
Reproduction steps: expected results
file: output.txtmydata.csv
Calculated: bar
OS & version + platform
Ubuntu 22 in GAE (python313)
Python environment
Python 3.13
Python dependencies
No response
Additional context
No response
Activity
lyushher commentedon Aug 1, 2026
Hi! I'd like to work on this if no one else is already working on it.
fix(ndb): avoid unbound lock in delete callback (#17969)