Skip to content

FIX make adapter injection atomic on validation failure - #3981

Open
eSVeeF wants to merge 2 commits into
huggingface:mainfrom
eSVeeF:fix/atomic-adapter-injection
Open

eSVeeF wants to merge 2 commits into
huggingface:mainfrom
eSVeeF:fix/atomic-adapter-injection

Conversation

@eSVeeF

@eSVeeF eSVeeF commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

This makes adapter injection validate all targets before mutating the model.

Previously, if an earlier target was valid but a later target failed validation, PEFT could leave the model partially modified. This was especially noticeable when dynamically adding adapters: the configuration was rolled back, but adapter parameters from the failed injection could remain in the model.

Changes

  • Split target discovery and validation from adapter creation.
  • Validate architecture-specific target compatibility before injecting any modules.
  • Preserve existing target-parameter conflict validation.
  • Preserve tied-module injection ordering.
  • Add regression tests for both initial injection and add_adapter().

Tests

  • tests/test_tuners_utils.py: 267 passed, 16 skipped
  • tests/test_initialization.py: 449 passed, 2 xfailed
  • tests/test_custom_models.py: 18,977 passed, 1,046 skipped
  • tests/test_mixed.py tests/test_other.py: 98 passed, 1 skipped
  • Focused rollback tests: 2 passed

Along the way, the broader suites caught a couple of ordering issues around target parameters and tied modules, so I kept the full-suite results here instead of relying only on some subsets.

Closes #3707

This PR was authored by a human. And written too.

@githubnemo githubnemo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the PR. At first glance the changes look sensible. I'm unsure about the injection order regarding tied modules, left a comment about that.

Comment thread src/peft/tuners/tuners_utils.py Outdated
# Only applicable if `ensure_weight_tying = True` for LoraConfig
if self._check_tied_module_exists(peft_config, key):
targets_to_tie.append(key)
targeted_module_names.append(key)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you explain to me why you moved the append here? Before the tied modules were added last to the targeted_module_names list, now they are getting added earlier if I'm not mistaken.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh, true, true.

I moved the append earlier so tied targets would be included in the pre-injection validation, but I didn't notice that it would change the ordering of targeted_module_names.

I鈥檒l keep tied targets separate during discovery, validate them before any mutation, and append them at the original point so normal targets remain first and tied targets remain last. You think is worth adding a regression test asserting that ordering?

Comment on lines +1007 to +1022
if getattr(peft_config, "target_parameters", []):
target_parameter_names = set(peft_config.target_parameters)
for module_name in module_names_to_inject + targets_to_tie:
module = model.get_submodule(module_name)
for parameter_name, _ in module.named_parameters(recurse=False):
key = f"{module_name}.{parameter_name}"
if (key in target_parameter_names) or any(
key.endswith(f".{target_name}") for target_name in target_parameter_names
):
raise ValueError(
f"Trying to wrap an `nn.Parameter` of layer '{module_name}' of type "
f"{type(module).__name__}, which is not a valid target. Make sure that this layer is not "
"also targeted with `target_modules`. For some models, PEFT will do this automatically, "
"try setting `target_modules=[]` to prevent it."
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's also add a short comment about the specific intent of this block, i.e. # Validate potential collisions between targeted parameter names targeted module names and let's refactor the raising of the exception into its own helper function which we then use in create_and_replace_param as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed adapter injection leaves orphaned adapter layers behind

3 participants