signalapp/Signal-Server · error · DeviceLimitExceededException
return Response.status(411)
Error message
return Response.status(411)
What it means
The DeviceLimitExceededExceptionMapper converts a DeviceLimitExceededException into HTTP 411 (Length Required — Signal reuses this code) with a JSON body of DeviceLimitExceededDetails containing currentDevices and maxDevices. It signals the account has hit the server's maximum number of linked devices, so a new device link/provision request is rejected.
Solutions
- Unlink existing devices (via the primary device's linked-devices screen or the device API) before linking a new one
- Reuse an existing linked device instead of provisioning another
- Raise the max device limit in server configuration if legitimate use requires it
- Audit for scripts leaking device provisioning calls in a loop
Example fix
// before
linkNewDevice(account);
// after
if (deviceList.size() >= maxDevices) {
unlinkOldestDevice(deviceList);
}
linkNewDevice(account); Defensive patterns
Strategy: validation
Validate before calling
// count linked devices before attempting to provision a new one
if (account.getDevices().size() >= serverMaxDevices) {
throw new IllegalStateException("unlink a device first");
} Type guard
boolean canLinkDevice(Account a, int maxDevices) {
return a.getDevices().size() < maxDevices;
} Try / catch
try { linkDevice(account); }
catch (DeviceLimitExceededException e) {
showUserDeviceList(e.getCurrentDevices(), e.getMaxDevices()); // prompt unlink
} Prevention
- Surface the linked-device list and limit in your UI
- Unlink dormant desktop/tablet sessions periodically
- Avoid automated loops that provision devices
- Raise server max-devices config only deliberately
When it happens
Trigger: Any endpoint that throws DeviceLimitExceededException — typically attempting to provision/link a new device when the account already has maxDevices linked devices.
Common situations: Users accumulating dormant linked desktops/tablets over years; automations repeatedly provisioning test devices against one account; low maxDevices setting in server config making the limit easy to hit.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- return Response.status(429).build();
- return Response.status(404).build();
- return Response.status(503).build();
- return Response.status(422).build();
- Empty body not allowed
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/f18a757a17f5d081.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/mappers/DeviceLimitExceededExceptionMapper.java:19
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.mappers;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import org.whispersystems.textsecuregcm.controllers.DeviceLimitExceededException;
@Provider
public class DeviceLimitExceededExceptionMapper implements ExceptionMapper<DeviceLimitExceededException> {
@Override
public Response toResponse(DeviceLimitExceededException exception) {
return Response.status(411)
.entity(new DeviceLimitExceededDetails(exception.getCurrentDevices(),
exception.getMaxDevices()))
.build();
}
private static class DeviceLimitExceededDetails {
@JsonProperty
private int current;
@JsonProperty
private int max;
public DeviceLimitExceededDetails(int current, int max) {
this.current = current;
this.max = max;
}
}
}
View on GitHub (pinned to 100ab61c82)