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

  1. Use exactly 'tenant' or 'system' (lowercase) in the scope path segment
  2. Normalize/trim the scope value and validate it against the two literals before building the URL
  3. 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

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


AI-assisted analysis of thingsboard/thingsboard@45c30e83fa (2026-08-14). Data as JSON: /api/errors/02c90ca658f4d531. Report an issue: GitHub.