Skip to content

Fix: validate trainable field type in Layer.__init__#22722

Closed
Jah-yee wants to merge 2 commits intokeras-team:masterfrom
Jah-yee:fix-trainable-validation
Closed

Fix: validate trainable field type in Layer.__init__#22722
Jah-yee wants to merge 2 commits intokeras-team:masterfrom
Jah-yee:fix-trainable-validation

Conversation

@Jah-yee
Copy link
Copy Markdown

@Jah-yee Jah-yee commented Apr 19, 2026

Good day

Issue

deserialize_keras_object accepts non-boolean values for the trainable field without validation (#22699).

Reproduction

from keras.saving import deserialize_keras_object
config = {
    "class_name": "Dense",
    "module": "keras.layers",
    "config": {
        "units": 32,
        "trainable": "yes"  # string instead of bool
    }
}
layer = deserialize_keras_object(config)
print(type(layer.trainable), layer.trainable)  # str, "yes"

Fix

Added an explicit type check in Layer.__init__ that raises a clear TypeError when trainable is not a bool:

if not isinstance(trainable, bool):
    raise TypeError(
        f"Expected `trainable` to be a bool, but got "
        f"{type(trainable).__name__}: {trainable!r}"
    )

This ensures validation happens at layer construction time (during deserialization), catching the bad type early with a descriptive error.

Testing

Verified that valid bool values (True/False) work correctly, while invalid values like "yes", 1, 0, etc. now raise TypeError as expected.

Thank you for your attention. If there are any issues or suggestions, please leave a comment and I will address them promptly.

Warmly,
RoomWithOutRoof

RoomWithOutRoof added 2 commits April 19, 2026 19:53
When deserialize_keras_object was called with a config dict where
the 'config' field was not a dict (e.g., a list or string), the
code would raise a confusing AttributeError: 'list' object has no
attribute 'get'. This was because from_config() expects a dict.

Now raises a clear TypeError:
  TypeError: Expected 'config' to be a dict, but got list: [1, 2, 3]

The fix is applied only for class deserialization (not for
'class_name == function' where inner_config is a string name).

Fixes keras-team#22701
Reject non-boolean trainable values with a clear TypeError at layer
construction time instead of silently accepting them.

Fixes keras-team#22699
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 19, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces input validation for the trainable parameter in layers and the config dictionary during serialization to ensure they are of the correct types. The review feedback correctly points out that ValueError should be used instead of TypeError for these user input validations, adhering to the Keras Repository Style Guide.

Comment thread keras/src/layers/layer.py
Comment on lines +300 to +304
if not isinstance(trainable, bool):
raise TypeError(
f"Expected `trainable` to be a bool, but got "
f"{type(trainable).__name__}: {trainable!r}"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Keras Repository Style Guide (line 178) specifies that ValueError should be used for user input validation instead of TypeError. Additionally, consider that isinstance(trainable, bool) is quite strict and will reject 0 or 1, which are often used as boolean substitutes in Python. However, if strict boolean enforcement is intended to prevent common configuration errors (like the string "False" being truthy), please at least update the exception type to ValueError.

Suggested change
if not isinstance(trainable, bool):
raise TypeError(
f"Expected `trainable` to be a bool, but got "
f"{type(trainable).__name__}: {trainable!r}"
)
if not isinstance(trainable, bool):
raise ValueError(
f"Expected `trainable` to be a bool, but got "
f"{type(trainable).__name__}: {trainable!r}"
)
References
  1. In general, to validate user input, always use ValueError and avoid assert. (link)

Comment on lines +624 to +632
if (
inner_config is not None
and not isinstance(inner_config, dict)
and class_name != "function"
):
raise TypeError(
f"Expected `config` to be a dict, but got {type(inner_config).__name__}: "
f"{inner_config}"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Please use ValueError instead of TypeError to adhere to the Keras Repository Style Guide (line 178).

Suggested change
if (
inner_config is not None
and not isinstance(inner_config, dict)
and class_name != "function"
):
raise TypeError(
f"Expected `config` to be a dict, but got {type(inner_config).__name__}: "
f"{inner_config}"
)
if (
inner_config is not None
and not isinstance(inner_config, dict)
and class_name != "function"
):
raise ValueError(
f"Expected `config` to be a dict, but got {type(inner_config).__name__}: "
f"{inner_config}"
)
References
  1. In general, to validate user input, always use ValueError and avoid assert. (link)

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 19, 2026

Codecov Report

❌ Patch coverage is 33.33333% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.95%. Comparing base (e94cb07) to head (25a0477).

Files with missing lines Patch % Lines
keras/src/layers/layer.py 0.00% 1 Missing and 1 partial ⚠️
keras/src/saving/serialization_lib.py 50.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #22722      +/-   ##
==========================================
- Coverage   82.95%   82.95%   -0.01%     
==========================================
  Files         596      596              
  Lines       69252    69257       +5     
  Branches    10814    10816       +2     
==========================================
+ Hits        57451    57452       +1     
- Misses       8973     8975       +2     
- Partials     2828     2830       +2     
Flag Coverage Δ
keras 82.77% <33.33%> (-0.01%) ⬇️
keras-jax 58.71% <33.33%> (-0.01%) ⬇️
keras-numpy 54.56% <33.33%> (-0.01%) ⬇️
keras-openvino 59.42% <33.33%> (-0.01%) ⬇️
keras-tensorflow 60.28% <33.33%> (-0.01%) ⬇️
keras-torch 59.05% <33.33%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@keerthanakadiri
Copy link
Copy Markdown
Contributor

Hi @Jah-yee, Can you please sign the CLA? Thank you!

@hertschuh
Copy link
Copy Markdown
Collaborator

This issue was addressed by #22740

@hertschuh hertschuh closed this Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants