nextcloud/server · error · Sabre\DAV\Exception\BadRequest

{http://nextcloud.com/ns}prop-filter or {http://nextcloud.co

Error message

{http://nextcloud.com/ns}prop-filter or {http://nextcloud.com/ns}param-filter given without any {http://nextcloud.com/ns}comp-filter

What it means

Structural check in CalendarSearchReport::xmlDeserialize(): the parsed filter contains prop-filters or param-filters but no comp-filter, so the server cannot know which component type (VEVENT/VTODO/VJOURNAL) to search and answers Sabre\DAV\Exception\BadRequest (HTTP 400). prop-filter and param-filter are only meaningful nested under a comp-filter.

Source

Thrown at apps/dav/lib/CalDAV/Search/Xml/Request/CalendarSearchReport.php:137

					break;
				case '{' . SearchPlugin::NS_Nextcloud . '}limit':
					$newProps['limit'] = $elem['value'];
					break;
				case '{' . SearchPlugin::NS_Nextcloud . '}offset':
					$newProps['offset'] = $elem['value'];
					break;

			}
		}

		if (empty($newProps['filters'])) {
			throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}filter element is required for this request');
		}

		$propsOrParamsDefined = (!empty($newProps['filters']['props']) || !empty($newProps['filters']['params']));
		$noCompsDefined = empty($newProps['filters']['comps']);
		if ($propsOrParamsDefined && $noCompsDefined) {
			throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter given without any {' . SearchPlugin::NS_Nextcloud . '}comp-filter');
		}

		if (!isset($newProps['filters']['search-term'])) {
			throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}search-term is required for this request');
		}

		if (empty($newProps['filters']['props']) && empty($newProps['filters']['params'])) {
			throw new BadRequest('At least one{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter is required for this request');
		}

		$obj = new self();
		foreach ($newProps as $key => $value) {
			$obj->$key = $value;
		}
		return $obj;
	}
}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Add a comp-filter naming the component type around/next to your prop and param filters
  2. Default the component selector to VEVENT in your UI when the user does not choose
  3. Validate the filter tree client-side: props/params present implies at least one comps entry

Example fix

// before
<n:filter>
  <n:prop-filter name="SUMMARY"/>
  <n:search-term>meeting</n:search-term>
</n:filter>

// after
<n:filter>
  <n:comp-filter name="VEVENT"/>
  <n:prop-filter name="SUMMARY"/>
  <n:search-term>meeting</n:search-term>
</n:filter>
Defensive patterns

Strategy: validation

Validate before calling

$hasComps = $comps !== [];
$hasPropsOrParams = $props !== [] || $params !== [];
if ($hasPropsOrParams && !$hasComps) {
    $comps = ['VEVENT']; // or throw: prop/param filters require a comp-filter
}

Type guard

function isValidFilterShape(array $comps, array $props, array $params): bool {
    return ($props === [] && $params === []) || $comps !== [];
}

Try / catch

try {
    $client->request('REPORT', $calendarUri, $xmlBody);
} catch (\Sabre\Http\ClientException $e) {
    if ($e->getResponse()?->getStatus() === 400) { /* add comp-filter for the component type */ }
}

Prevention

When it happens

Trigger: Sending <n:filter><n:prop-filter name="SUMMARY"/><n:search-term>x</n:search-term></n:filter> without a <n:comp-filter name="VEVENT"/>.

Common situations: Client search forms that let users pick properties but no component type; copy-pasted payload fragments from a larger example losing the outer comp-filter.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/78b0c1cd2614448c. Report an issue: GitHub.