diff --git a/atools/_memoize_decorator.py b/atools/_memoize_decorator.py
index ac7e47b..ffe8028 100644
--- a/atools/_memoize_decorator.py
+++ b/atools/_memoize_decorator.py
@@ -123,11 +123,14 @@ class _MemoizeBase:
 
     def default_keygen(self, *args, **kwargs) -> Tuple[Hashable, ...]:
         """Returns all params (args, kwargs, and missing default kwargs) for function as kwargs."""
+
+        return tuple(self.get_args_as_kwargs(*args, **kwargs).values())
+
+    def get_args_as_kwargs(self, *args, **kwargs) -> Mapping[str, Any]:
         args_as_kwargs = {}
         for k, v in zip(self.default_kwargs, args):
             args_as_kwargs[k] = v
-
-        return tuple(ChainMap(args_as_kwargs, kwargs, self.default_kwargs).values())
+        return ChainMap(args_as_kwargs, kwargs, self.default_kwargs)
 
     def get_memo(self, key: Union[int, str]) -> _Memo:
         try:
@@ -221,7 +224,7 @@ class _AsyncMemoize(_MemoizeBase):
         if self.keygen is None:
             key = self.default_keygen(*args, **kwargs)
         else:
-            key = self.keygen(*args, **kwargs)
+            key = self.keygen(**self.get_args_as_kwargs(*args, **kwargs))
             if isinstance(key, tuple):
                 key = list(key)
             else:
@@ -277,7 +280,7 @@ class _SyncMemoize(_MemoizeBase):
         if self.keygen is None:
             key = self.default_keygen(*args, **kwargs)
         else:
-            key = self.keygen(*args, **kwargs)
+            key = self.keygen(**self.get_args_as_kwargs(*args, **kwargs))
 
         key = self.get_hashed_key(key)
 
diff --git a/setup.py b/setup.py
index ee2987c..4e6d25d 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
 
 setup(
     name='atools',
-    version='0.10.4',
+    version='0.10.5',
     packages=find_packages(),
     python_requires='>=3.6',
     url='https://github.com/cevans87/atools',

memoize keygen breaks when caller omits keyword args with defaults
Ex

```
@memoize(keygen=lambda bar: bar)
def foo(bar=None):
    ...
foo(None)  # Works fine
foo()  # Results in `TypeError: <lambda>() missing 1 required positional argument: 'bar'`
diff --git a/test/test_memoize_decorator.py b/test/test_memoize_decorator.py
index d072da2..d4cba68 100644
--- a/test/test_memoize_decorator.py
+++ b/test/test_memoize_decorator.py
@@ -919,3 +919,11 @@ def test_memoized_function_is_deletable() -> None:
     del foo
     # FIXME there's a race condition here. Garbage collector may not have cleaned up foo yet
     assert r() is None
+
+
+def test_keygen_works_with_default_kwargs() -> None:
+    @memoize(keygen=lambda bar: bar)
+    def foo(bar=1) -> None:
+        ...
+
+    foo()

pip install -e .[u2f]
parse_log_pytest
requirements.txt
nose mock pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/cevans87/atools.git@ef5ba0a45ac3fd8a0370e7db41c0acdc90354165#egg=atools
attrs==22.2.0
certifi==2021.5.30
dataclasses==0.8
frozendict==2.4.6
importlib-metadata==4.8.3
iniconfig==1.1.1
mock==5.2.0
nose==1.3.7
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: atools
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - dataclasses==0.8
      - frozendict==2.4.6
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - mock==5.2.0
      - nose==1.3.7
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/atools

test/test_memoize_decorator.py::test_keygen_works_with_default_kwargs
test/test_memoize_decorator.py::test_zero_args test/test_memoize_decorator.py::test_class_function test/test_memoize_decorator.py::test_keyword_same_as_default test/test_memoize_decorator.py::test_sync_size test/test_memoize_decorator.py::test_sync_exception test/test_memoize_decorator.py::test_expire_current_call test/test_memoize_decorator.py::test_expire_old_call test/test_memoize_decorator.py::test_expire_old_item_does_not_expire_new test/test_memoize_decorator.py::test_expire_head_of_line_refresh_does_not_stop_eviction test/test_memoize_decorator.py::test_size_le_zero_raises test/test_memoize_decorator.py::test_expire_le_zero_raises test/test_memoize_decorator.py::test_args_overlaps_kwargs_raises test/test_memoize_decorator.py::test_sync_reset_clears_cache test/test_memoize_decorator.py::test_works_with_property test/test_memoize_decorator.py::test_sync_locks_sync test/test_memoize_decorator.py::test_sync_does_not_async_lock test/test_memoize_decorator.py::test_async_no_event_loop_does_not_raise test/test_memoize_decorator.py::test_memoizes_class test/test_memoize_decorator.py::test_memoizes_class_with_metaclass test/test_memoize_decorator.py::test_reset_all_resets_class_decorators test/test_memoize_decorator.py::test_reset_all_resets_function_decorators test/test_memoize_decorator.py::test_memoize_class_preserves_doc test/test_memoize_decorator.py::test_keygen_overrides_default test/test_memoize_decorator.py::test_db_creates_table_for_each_decorator[bool] test/test_memoize_decorator.py::test_db_creates_table_for_each_decorator[connect] test/test_memoize_decorator.py::test_db_creates_table_for_each_decorator[Path] test/test_memoize_decorator.py::test_db_creates_table_for_each_decorator[str] test/test_memoize_decorator.py::test_db_reloads_values_from_disk[bool] test/test_memoize_decorator.py::test_db_reloads_values_from_disk[connect] test/test_memoize_decorator.py::test_db_reloads_values_from_disk[Path] test/test_memoize_decorator.py::test_db_reloads_values_from_disk[str] test/test_memoize_decorator.py::test_reset_removes_values_on_disk[bool] test/test_memoize_decorator.py::test_reset_removes_values_on_disk[connect] test/test_memoize_decorator.py::test_reset_removes_values_on_disk[Path] test/test_memoize_decorator.py::test_reset_removes_values_on_disk[str] test/test_memoize_decorator.py::test_db_expires_memo[bool] test/test_memoize_decorator.py::test_db_expires_memo[connect] test/test_memoize_decorator.py::test_db_expires_memo[Path] test/test_memoize_decorator.py::test_db_expires_memo[str] test/test_memoize_decorator.py::test_db_memoizes_multiple_values[bool] test/test_memoize_decorator.py::test_db_memoizes_multiple_values[connect] test/test_memoize_decorator.py::test_db_memoizes_multiple_values[Path] test/test_memoize_decorator.py::test_db_memoizes_multiple_values[str] test/test_memoize_decorator.py::test_db_with_size_expires_lru[bool] test/test_memoize_decorator.py::test_db_with_size_expires_lru[connect] test/test_memoize_decorator.py::test_db_with_size_expires_lru[Path] test/test_memoize_decorator.py::test_db_with_size_expires_lru[str] test/test_memoize_decorator.py::test_db_with_duration_expires_stale_values[bool] test/test_memoize_decorator.py::test_db_with_duration_expires_stale_values[connect] test/test_memoize_decorator.py::test_db_with_duration_expires_stale_values[Path] test/test_memoize_decorator.py::test_db_with_duration_expires_stale_values[str] test/test_memoize_decorator.py::test_db_memoizes_frozenset[bool] test/test_memoize_decorator.py::test_db_memoizes_frozenset[connect] test/test_memoize_decorator.py::test_db_memoizes_frozenset[Path] test/test_memoize_decorator.py::test_db_memoizes_frozenset[str] test/test_memoize_decorator.py::test_sync_reset_call_resets_one test/test_memoize_decorator.py::test_reset_call_before_expire_resets_one test/test_memoize_decorator.py::test_reset_call_with_db_resets_call[bool] test/test_memoize_decorator.py::test_reset_call_with_db_resets_call[connect] test/test_memoize_decorator.py::test_reset_call_with_db_resets_call[Path] test/test_memoize_decorator.py::test_reset_call_with_db_resets_call[str] test/test_memoize_decorator.py::test_set_default_db_path_uses_given_path test/test_memoize_decorator.py::test_db_can_return_type_of_return_hint[bool] test/test_memoize_decorator.py::test_db_can_return_type_of_return_hint[connect] test/test_memoize_decorator.py::test_db_can_return_type_of_return_hint[Path] test/test_memoize_decorator.py::test_db_can_return_type_of_return_hint[str] test/test_memoize_decorator.py::test_db_can_return_type_of_callers_globals[bool] test/test_memoize_decorator.py::test_db_can_return_type_of_callers_globals[connect] test/test_memoize_decorator.py::test_db_can_return_type_of_callers_globals[Path] test/test_memoize_decorator.py::test_db_can_return_type_of_callers_globals[str] test/test_memoize_decorator.py::test_memoized_function_is_deletable
atools/_memoize_decorator.py setup.py
atools/_memoize_decorator.py setup.py