diff --git a/actions_resource.go b/actions_resource.go
index 12bee82e8392a3038d73f6c5f75b1bc806d1baaf..012a72865c452dcdd47a47734cbbf17afde6c1d5 100644
--- a/actions_resource.go
+++ b/actions_resource.go
@@ -1,7 +1,6 @@
 package accountserver
 
 import (
-	"errors"
 	"fmt"
 	"log"
 )
@@ -196,7 +195,7 @@ func (r *CheckResourceAvailabilityRequest) Validate(rctx *RequestContext) error
 func (r *CheckResourceAvailabilityRequest) Serve(rctx *RequestContext) (interface{}, error) {
 	var check ValidatorFunc
 	switch r.Type {
-	case ResourceTypeEmail, ResourceTypeMailingList:
+	case ResourceTypeEmail, ResourceTypeMailingList, ResourceTypeNewsletter:
 		check = rctx.validationCtx.isAvailableEmailAddr()
 	case ResourceTypeDomain:
 		check = rctx.validationCtx.isAvailableDomain()
@@ -207,7 +206,7 @@ func (r *CheckResourceAvailabilityRequest) Serve(rctx *RequestContext) (interfac
 	case ResourceTypeDatabase:
 		check = rctx.validationCtx.isAvailableDatabase()
 	default:
-		return nil, errors.New("unknown resource type")
+		return nil, newValidationError(nil, "type", "unknown resource type")
 	}
 
 	var resp CheckResourceAvailabilityResponse
diff --git a/backend/ldap/resources.go b/backend/ldap/resources.go
index b20ea52533b7a5c251627efce0ff6a02148784b0..be97114ba4be1d32836ce030199b6f1d8de22ea0 100644
--- a/backend/ldap/resources.go
+++ b/backend/ldap/resources.go
@@ -100,7 +100,8 @@ func setCommonResourceAttrs(entry *ldap.Entry, rsrc *as.Resource) {
 func (reg *resourceRegistry) FromLDAP(entry *ldap.Entry) (rsrc *as.Resource, err error) {
 	// Since we don't know what resource type to expect, we try
 	// all known handlers until one returns a valid Resource.
-	// This is slightly dangerous unless all
+	// This expects that all object types can be told apart by
+	// their DN or attributes.
 	for _, h := range reg.handlers {
 		rsrc, err = h.FromLDAP(entry)
 		if err == nil {
diff --git a/integrationtest/availability_test.go b/integrationtest/availability_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ec6f29873355c8d2490ce543c16ef533b605d4e
--- /dev/null
+++ b/integrationtest/availability_test.go
@@ -0,0 +1,52 @@
+package integrationtest
+
+import (
+	"testing"
+
+	as "git.autistici.org/ai3/accountserver"
+)
+
+func TestIntegration_CheckResourceAvailability(t *testing.T) {
+	stop, _, c := startService(t)
+	defer stop()
+
+	testdata := []struct {
+		rtype         string
+		name          string
+		expectedAvail bool
+	}{
+		{"email", "uno@investici.org", false},
+		{"email", "due@investici.org", false},
+		{"email", "tre@investici.org", false},
+		{"email", "newsletter1@investici.org", false},
+		{"email", "list4@investici.org", false},
+		{"email", "new@investici.org", true},
+
+		{"list", "newsletter1@investici.org", false},
+		{"list", "list4@investici.org", false},
+
+		{"newsletter", "newsletter1@investici.org", false},
+		{"newsletter", "list4@investici.org", false},
+
+		{"web", "due", false},
+		{"web", "new", true},
+
+		{"dav", "uno", false},
+		{"dav", "new", true},
+	}
+
+	for _, td := range testdata {
+		var resp as.CheckResourceAvailabilityResponse
+		err := c.request("/api/resource/check_availability", &as.CheckResourceAvailabilityRequest{
+			Type: td.rtype,
+			Name: td.name,
+		}, &resp)
+		if err != nil {
+			t.Errorf("CheckResourceAvailability(%s/%s) error: %v", td.rtype, td.name, err)
+			continue
+		}
+		if resp.Available != td.expectedAvail {
+			t.Errorf("CheckResourceAvailability(%s/%s) returned %v, expected %v", td.rtype, td.name, resp.Available, td.expectedAvail)
+		}
+	}
+}
diff --git a/integrationtest/testdata/test4.ldif b/integrationtest/testdata/test4.ldif
index a5c72451a1f67d32fd5982d870a5815674d882a3..283e2b63712ac60f6c854fe3a77d336d08ec6125 100644
--- a/integrationtest/testdata/test4.ldif
+++ b/integrationtest/testdata/test4.ldif
@@ -41,3 +41,14 @@ status: active
 uidNumber: 23801
 userPassword:: JGEyJDQkMzI3NjgkMSQwZDgyMzU1YjQ0Mzg0M2NmZDY4MjU1MzE4ZTVjYTdiZSRmNTQ0ODkxOTFiNWZlYzk2MDRlNWQ2ODZjMDQxZjJkNTFmOTgxOGY4ZTFmM2E4MDYzY2U3ZTEwMTE3OTc2OGI0
 
+dn: listName=list4@investici.org,ou=Lists,dc=example,dc=com
+objectClass: mailingList
+objectClass: top
+listName: list4@investici.org
+listDescription: Mailing list 4
+public: no
+status: active
+listOwner: quattro@investici.org
+host: host2
+originalHost: host2
+
diff --git a/server/server.go b/server/server.go
index c4890f05a3e2d908e82599bb51ae4974469d66b7..214753d3378d8cfd6b8bcd4e3fdf8ca5421171cb 100644
--- a/server/server.go
+++ b/server/server.go
@@ -251,6 +251,8 @@ func errToStatus(err error) int {
 		return http.StatusForbidden
 	case as.IsRequestError(err):
 		return http.StatusBadRequest
+	case as.IsValidationError(err):
+		return http.StatusBadRequest
 	default:
 		return http.StatusInternalServerError
 	}
diff --git a/validators.go b/validators.go
index adff902f0d76ba4835103c60c297f8d0400cc59a..cd169d6b1df0c30b5035f532d51766ceebcb199a 100644
--- a/validators.go
+++ b/validators.go
@@ -349,14 +349,18 @@ func splitEmailAddr(addr string) (string, string) {
 // Returns all the possible resources in the email and mailing list
 // namespaces that might conflict with the given email address.
 func relatedEmails(ctx context.Context, be domainBackend, addr string) []FindResourceRequest {
+	// Check for the literal addr in the unified mail+list namespace.
 	rel := []FindResourceRequest{
 		{Type: ResourceTypeEmail, Name: addr},
+		{Type: ResourceTypeMailingList, Name: addr},
+		{Type: ResourceTypeNewsletter, Name: addr},
 	}
-	user, _ := splitEmailAddr(addr)
+
 	// Mailing lists and newsletters must have unique names
 	// regardless of the domain, so we add potential conflicts for
 	// mailing lists with the same name over all list-enabled
 	// domains.
+	user, _ := splitEmailAddr(addr)
 	for _, d := range be.GetAllowedDomains(ctx, ResourceTypeMailingList) {
 		rel = append(rel, FindResourceRequest{
 			Type: ResourceTypeMailingList,