apereo/cas · error

Could not extract registered services from request body

Error message

Could not extract registered services from request body

What it means

importSingleService() reads the raw HTTP request body (expected to be a serialized registered service in JSON or another supported format) and rejects the request with HTTP 400 when the body is blank. CAS cannot import a service definition from an empty payload, so it logs this warning and returns badRequest without touching the registry.

Solutions

  1. Send the service JSON as the raw request body, e.g. curl --data-binary @service.json -H 'Content-Type: application/json'
  2. Log/print the request body (CAS already traces it) to confirm the server actually receives content
  3. Check for filters/proxies that read or buffer the request InputStream before it reaches the endpoint
  4. Ensure the HTTP method is POST/PUT with a body, not a GET

Example fix

// before: empty body
curl -X POST https://cas/v1/services/import
// after
curl -X POST https://cas/v1/services/import \
  -H 'Content-Type: application/json' \
  --data-binary @service.json
Defensive patterns

Strategy: validation

Validate before calling

// before sending
const body = fs.readFileSync('service.json', 'utf8');
if (!body.trim()) throw new Error('service definition body is empty');

Type guard

function hasBody(req) { return req.body != null && String(req.body).trim().length > 0; }

Try / catch

val resp = post(importUrl, body);
if (resp.status === 400) throw new Error('import rejected: empty/invalid body');

Prevention

When it happens

Trigger: POSTing to the service import endpoint with an empty body, whitespace-only body, or when the request InputStream yields nothing (e.g. body not forwarded by a proxy or wrong Content-Type so the stream is already consumed).

Common situations: curl call missing -d/--data-binary; sending the service definition as multipart/form-data instead of a raw body; a reverse proxy or servlet filter consuming the InputStream before CAS reads it; copy-paste of an empty file.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/003ec2919989e602. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-reports-core/src/main/java/org/apereo/cas/web/report/RegisteredServicesEndpoint.java:416

    })
    @ResponseBody
    @Operation(summary = "Update registered service supplied in the request body",
        parameters = @Parameter(name = "body", required = true, description = "The request body to contain service definition"))
    public ResponseEntity updateService(
        @RequestBody
        final String registeredServiceBody) {
        val registeredServiceSerializer = new RegisteredServiceJsonSerializer(applicationContext);
        val registeredService = registeredServiceSerializer.from(registeredServiceBody);
        val result = servicesManager.getObject().save(registeredService);
        return ResponseEntity.ok(registeredServiceSerializer.toString(result));
    }

    private ResponseEntity<RegisteredService> importSingleService(final HttpServletRequest request) throws IOException {
        val requestBody = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        LOGGER.trace("Submitted registered service:\n[{}]", requestBody);

        if (StringUtils.isBlank(requestBody)) {
            LOGGER.warn("Could not extract registered services from request body");
            return ResponseEntity.badRequest().build();
        }

        return registeredServiceSerializers
            .getObject()
            .stream()
            .map(serializer -> serializer.from(requestBody))
            .filter(Objects::nonNull)
            .findFirst()
            .map(service -> {
                LOGGER.trace("Storing registered service:\n[{}]", service);
                return servicesManager.getObject().save(service);
            })
            .map(service -> {
                val headers = new HttpHeaders();
                headers.put("id", CollectionUtils.wrapList(String.valueOf(service.getId())));
                return new ResponseEntity<>(service, headers, HttpStatus.CREATED);
            })

View on GitHub (pinned to e7288fc434)