huggingface / huggingface/peft
Failed adapter injection leaves orphaned adapter layers behind
- Dominant language
- Python
- Stars
- 21.7k
- Forks
- 2.5k
- Avg merge
- 4d 12h
- Merged PRs (30d)
- 59
Description
System Info
- Python: `3.12.3`
- PEFT: main `279fccce200686af324611d7c49e3adab60485e1`
- Accelerate: `1.13.0`
- Transformers: `5.3.0`
While adding adapters to a small Mamba-like model, I ran into a case where an invalid target caused `add_adapter()` to fail, but the model was still partially modified.
```python
import torch
from torch import nn
from peft import LoraConfig, get_peft_model
class TinyMambaLike(nn.Module):
def __init__(self):
super().__init__()
self.safe = nn.Linear(4, 4, bias=False)
self.out_proj = nn.Linear(4, 4, bias=False)
self.config = type("Config", (), {"model_type": "mamba"})()
def forward(self, x):
return self.out_proj(self.safe(x))
model = get_peft_model(
TinyMambaLike(),
LoraConfig(target_modules=["safe"], r=2),
adapter_name="default",
)
try:
model.add_adapter(
"other",
LoraConfig(target_modules=["safe", "out_proj"], r=2),
)
except ValueError as error:
print(error)
print(model.peft_config.keys())
print([name for name in model.state_dict() if ".other." in name])
```
The second configuration is rejected because `out_proj` is not supported for Mamba-based models. However, `safe` has already been updated before the error is raised.
Observed result:
```text
dict_keys(["default"])
[
"base_model.model.safe.lora_A.other.weight",
"base_model.model.safe.lora_B.other.weight",
]
```
The configuration is rolled back, but the partially injected `"other"` adapter remains in the model. The same general behavior also occurs during initial injection: an error on a later target can leave earlier targets replaced.
It looks like `BaseTuner.inject_adapter()` performs module replacement while traversing targets, with compatibility checks occurring as each target is processed. `PeftModel.add_adapter()` removes the new configuration when injection fails, but does not restore the module structure or tuner state.
I would expect a failed injection to leave the model unchanged, or at least to leave no adapter parameters that are no longer represented in `peft_config`.
I’d be happy to open a PR with regression coverage and a fix. But I'm not sure right now if we should validate all targets before mutating the model, or implemente a rollback mechanism that restores the module and tuner state if any injection step raises.
Contributor guide
Research direction
Reproduce the supplied Mamba-like example first, then read BaseTuner.inject_adapter() and PeftModel.add_adapter(), where the issue reports mutation and configuration cleanup. Add regression coverage showing that a failed injection leaves the model structure, tuner state, and peft_config unchanged, including failure on a later target.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, pytorch
- Domain
- machine-learning
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100