huggingface / huggingface/peft

Failed adapter injection leaves orphaned adapter layers behind

Open
#3,707 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.