thingsboard/thingsboard · error · IllegalArgumentException
Invalid scope
Error message
Invalid scope
What it means
checkResourceInfo in TbResourceController resolves the owning tenant from a scope string: 'tenant' → current tenant, 'system' → SYS_TENANT_ID. Anything else throws IllegalArgumentException('Invalid scope') → HTTP 400. Used by resource download endpoints like /api/resource/{resourceType}/{scope}/{key}.
Source
Thrown at application/src/main/java/org/thingsboard/server/controller/TbResourceController.java:469
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + resourceInfo.getFileName())
.header("x-filename", resourceInfo.getFileName())
.contentLength(resource.contentLength())
.header("Content-Type", resourceInfo.getResourceType().getMediaType())
.cacheControl(CacheControl.noCache())
.eTag(resourceInfo.getEtag())
.body(resource);
}
private TbResourceInfo checkResourceInfo(String scope, ResourceType resourceType, String key, Operation operation) throws ThingsboardException {
TenantId tenantId;
if (scope.equals("tenant")) {
tenantId = getTenantId();
} else if (scope.equals("system")) {
tenantId = TenantId.SYS_TENANT_ID;
} else {
throw new IllegalArgumentException("Invalid scope");
}
TbResourceInfo resourceInfo = resourceService.findResourceInfoByTenantIdAndKey(tenantId, resourceType, key);
checkEntity(getCurrentUser(), checkNotNull(resourceInfo), operation);
return resourceInfo;
}
}
View on GitHub (pinned to 45c30e83fa)
Solutions
- Use exactly 'tenant' or 'system' (lowercase) in the scope path segment
- Normalize/trim the scope value and validate it against the two literals before building the URL
- Check the OpenAPI spec for the endpoint to confirm allowed values
Example fix
// before
const url = `/api/resource/${type}/${scope}/${key}`; // scope = 'Tenant'
// after
const s = String(scope).trim().toLowerCase();
if (!['tenant', 'system'].includes(s)) throw new Error('scope must be tenant|system');
const url = `/api/resource/${type}/${s}/${key}`; Defensive patterns
Strategy: validation
Validate before calling
const s = scope?.trim().toLowerCase();
if (s !== 'tenant' && s !== 'system') throw new Error("scope must be 'tenant' or 'system'"); Type guard
function isResourceScope(v) { return v === 'tenant' || v === 'system'; } Try / catch
catch (e) { if (e.status === 400 && /Invalid scope/.test(e.message)) { normalizeScopeAndRetry(); } else throw e; } Prevention
- Lowercase and trim scope before interpolating into resource URLs
- Keep a constant list of the two allowed scope literals
When it happens
Trigger: GET /api/resource/JKS/tenant-resources/my-key, or scope values like 'Tenant', 'SYSTEM', 'global', 'all', or an empty path segment.
Common situations: URL templates built from enums or constants that don't match the exact lowercase literals; trailing whitespace or case differences from user-entered scope; clients ported from another resource API with different scope names.
Related errors
- Template is missing
- Target type is not platform users
- RpcStatus: DELETED
- BAD_REQUEST_PARAMS
- BAD_REQUEST_PARAMS
AI-assisted analysis of thingsboard/thingsboard@45c30e83fa (2026-08-14).
Data as JSON: /api/errors/02c90ca658f4d531.
Report an issue: GitHub.