thingsboard/thingsboard · warning · ThingsboardException
BAD_REQUEST_PARAMS
BAD_REQUEST_PARAMS
Error message
The device must be a gateway!
What it means
The gateway-launch docker-compose endpoint builds a compose file that provisions the target device as an IoT gateway. Only devices flagged as gateways (additionalInfo.gateway = true) are valid targets, so any non-gateway device id is rejected with BAD_REQUEST_PARAMS before the compose file is generated.
Source
Thrown at application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java:159
@ApiResponse(
responseCode = "200",
description = "OK",
content = @Content(
mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
schema = @Schema(type = "string", format = "binary")
)
)
})
@RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}/docker-compose/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadGatewayDockerCompose(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException, IOException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS);
if (!checkIsGateway(device)) {
throw new ThingsboardException("The device must be a gateway!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
var dockerCompose = checkNotNull(deviceConnectivityService.createGatewayDockerComposeFile(baseUrl, device), "Failed to create docker-compose.yml file!");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + DOCKER_COMPOSE_YML)
.header("x-filename", DOCKER_COMPOSE_YML)
.contentLength(dockerCompose.contentLength())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(dockerCompose);
}
private static boolean checkIsGateway(Device device) {
return device.getAdditionalInfo().has(DataConstants.GATEWAY_PARAMETER) &&
device.getAdditionalInfo().get(DataConstants.GATEWAY_PARAMETER).asBoolean();
}
}View on GitHub (pinned to 45c30e83fa)
Solutions
- Pick a device that is actually a gateway (device.additionalInfo.gateway == true).
- Convert the device: call saveDevice with additionalInfo {"gateway": true}.
- If a new gateway is needed, create it via POST /api/device with the gateway flag set, then retry the download.
Example fix
// before
Device device = new Device();
device.setName("sensor-1"); // no gateway flag -> download fails
// after
Device device = new Device();
device.setName("gw-1");
device.setAdditionalInfo(JacksonUtil.newObjectNode().put("gateway", true)); Defensive patterns
Strategy: type-guard
Validate before calling
Device device = deviceClient.getDeviceById(deviceId);
boolean gateway = device.getAdditionalInfo() != null && device.getAdditionalInfo().has("gateway") && device.getAdditionalInfo().get("gateway").asBoolean(); Type guard
boolean isGatewayDevice(Device d) {
return d.getAdditionalInfo() != null
&& d.getAdditionalInfo().path("gateway").asBoolean(false);
} Try / catch
try {
return connectivityClient.downloadGatewayDockerCompose(deviceId);
} catch (ThingsboardException e) {
if ("The device must be a gateway!".equals(e.getMessage())) {
return promptUserToCreateGateway(); // or set the flag then retry
}
throw e;
} Prevention
- Filter device pickers to gateway=true when launching gateway flows.
- Set additionalInfo.gateway=true at creation time for gateway devices.
- Provide a 'convert to gateway' action instead of failing the download.
When it happens
Trigger: GET /api/device-connectivity/gateway-launch/{deviceId}/docker-compose/download where the device was created without the 'Is gateway' flag — typically the user picked the wrong device or created it via API without additionalInfo.gateway=true.
Common situations: Provisioning a regular device and then trying to fetch gateway launch instructions for it; device created via REST/mqtt claim without the gateway flag; UI dropdown listing all devices instead of only gateways.
Related errors
AI-assisted analysis of thingsboard/thingsboard@45c30e83fa (2026-08-14).
Data as JSON: /api/errors/4eab7c8746ef60a8.
Report an issue: GitHub.