Repository: netbox-community/netbox

Issue:
Error message hidden on VLAN Group tab when editing a VLAN with both Group and Site configured
### NetBox version

v3.4.2

### Python version

3.8

### Steps to Reproduce

1. Create site named Site1 with the automatically assigned slug and all other settings as default
2. Create VLAN group named VLAN_Group with the automatically assigned slug and all other settings as default
3. Create new vlan id 4000 named VLAN_4000.  Add it to Site1, leaving all other settings default.
4. Edit the new vlan.  Add it to vlan group VLAN_Group and click Save.

### Expected Behavior

Page should reload with an error stating what the problem is (this can be seen when trying to create a new vlan with both a site assignment and a vlan group assignment).

### Observed Behavior

Page reloads with the error message hidden on the VLAN Group tab.


Discussion:
Thank you for opening a bug report. Unfortunately, the information you have provided is not sufficient for someone else to attempt to reproduce the reported behavior. Remember, each bug report must include detailed steps that someone else can follow on a clean, empty NetBox installation to reproduce the exact problem you're experiencing. These instructions should include the creation of any involved objects, any configuration changes, and complete accounting of the actions being taken. Also be sure that your report does not reference data on the public NetBox demo, as that is subject to change at any time by an outside party and cannot be relied upon for bug reports.
Sorry, I wasn't trying to be obtuse.  I can add steps such as "create a site", "create a vlan group" if needed.  Is that the level of detail you're looking for?
@jhammond-git you must include **detailed** steps that someone else can follow on **a clean, empty NetBox installation** to reproduce the exact problem you're experiencing.
I understand.  I obviously missed my mark and ended up way too far in the "brevity" end of the field.  Are the edited instructions sufficient?
> Page reloads with no indication of why the edit failed, or even that it failed, other than still being in the Edit form.

The error is in the VLAN Group "tab", you just have to switch to it:

![image](https://user-images.githubusercontent.com/400797/221108371-0d24c42a-c91e-4add-af2b-275bd33e16cf.png)

I guess the issue is that the tab isn't switched to the VLAN Group "tab", but I'm not sure how to best handle that to be honest. The active tab is the one that is set, which is still the Site "tab". I guess we could throw a generic validation error (instead or in addition), which should show up as a toast instead.

Could you please update the issue to correct the observed behavior and give the issue a proper title instead of "No error message when". Thanks.
Quick tangent:

> I guess the issue is that the tab isn't switched to the VLAN Group "tab", but I'm not sure how to best handle that to be honest.

Under the `feature` branch #11625 now gives us the ability to re-render forms based on a selected value. One option would be to change the tabs to a "type" selector and render only the fields relevant to the selected type. (I'm doing this already with the new DataSource model, which will include backend-specific configuration fields depending on the selected backend.) Probably worth a separate discussion.
Let me know if the edits don't cover what you wanted.  Not sure how I didn't finish the title the first time.
Marking this as blocked by #11891.

Resolving patch:
diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py
--- a/netbox/ipam/models/vlans.py
+++ b/netbox/ipam/models/vlans.py
@@ -224,11 +224,11 @@ def clean(self):
 
         # Validate VLAN group (if assigned)
         if self.group and self.site and self.group.scope != self.site:
-            raise ValidationError({
-                'group': _(
+            raise ValidationError(
+                _(
                     "VLAN is assigned to group {group} (scope: {scope}); cannot also assign to site {site}."
                 ).format(group=self.group, scope=self.group.scope, site=self.site)
-            })
+            )
 
         # Validate group min/max VIDs
         if self.group and not self.group.min_vid <= self.vid <= self.group.max_vid:


Test patch: