nextcloud/server · error · Sabre\DAV\Exception

An error occurred while checking content

Error message

An error occurred while checking content

What it means

Thrown by MultipartRequestParser::isAt() when fread() on the DAV request body returns false while peeking ahead for the multipart boundary. The bulk-upload parser must read and then seek back on the body stream, so any stream that cannot be read at the current cursor aborts with this generic Sabre\DAV\Exception. Note that isAtLastBoundary() runs in the while-condition of BulkUploadPlugin::httpPost, OUTSIDE its try/catch, so this escapes as an unhandled HTTP 500 instead of the endpoint's usual 400 JSON reply.

Source

Thrown at apps/dav/lib/BulkUpload/MultipartRequestParser.php:101

		if (trim($boundaryKey) !== 'boundary') {
			throw new BadRequest('Boundary is invalid');
		}

		return $boundaryValue;
	}

	/**
	 * Check whether the stream's cursor is sitting right before the provided string.
	 *
	 * @throws Exception
	 */
	private function isAt(string $expectedContent): bool {
		$expectedContentLength = strlen($expectedContent);

		$content = fread($this->stream, $expectedContentLength);
		if ($content === false) {
			throw new Exception('An error occurred while checking content');
		}

		$seekBackResult = fseek($this->stream, -$expectedContentLength, SEEK_CUR);
		if ($seekBackResult === -1) {
			throw new Exception('Unknown error while seeking content', Http::STATUS_INTERNAL_SERVER_ERROR);
		}

		return $expectedContent === $content;
	}

	/**
	 * Check whether the stream's cursor is sitting right before the boundary.
	 */
	private function isAtBoundary(): bool {
		return $this->isAt($this->boundary);
	}

	/**

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Reproduce with a known-good payload (curl or the Nextcloud desktop client) to rule out malformed multipart data
  2. Check the server log for the stack trace and identify which component touched $request->getBody() before OCA\DAV\BulkUpload\BulkUploadPlugin::httpPost
  3. Ensure nothing consumes the body earlier in the request lifecycle, or rewind it / rewrap it in a php://temp buffer before the bulk endpoint parses it
  4. If the stream resource is closed or invalid, fix the code that created or replaced the request body so a readable resource reaches the parser

Example fix

// before
$raw = stream_get_contents($request->getBody()); // body consumed; later fread() in isAt() fails

// after
$body = $request->getBody();
rewind($body); // let MultipartRequestParser read from the start
Defensive patterns

Strategy: try-catch

Validate before calling

$body = $request->getBody();
if (!is_resource($body) || !is_readable($body)) {
    throw new BadRequest('Request body is not a readable stream');
}
if (!feof($body)) {
    rewind($body); // ensure parser starts at the beginning
}

Try / catch

// BulkUploadPlugin calls isAtLastBoundary() OUTSIDE its try/catch - wrap it too:
try {
    while (!$parser->isAtLastBoundary()) {
        [$headers, $content] = $parser->parseNextPart();
    }
} catch (Sabre\DAV\Exception $e) {
    $this->logger->error($e->getMessage());
    $response->setStatus(500);
    return false;
}

Prevention

When it happens

Trigger: POST to /dav/bulk where the Sabre request body is unreadable at the cursor: the body stream was already consumed or closed by an earlier plugin/handler, a broken stream wrapper was set as body, or the client aborted mid-upload so the underlying socket read fails.

Common situations: Middleware or another Sabre plugin reading the body before the bulk plugin and not rewinding it; tests injecting a closed resource as body; flaky connections dropping during large multi-file uploads.

Related errors


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