nextcloud/server · error · Sabre\DAV\Exception
500
500
Error message
Unknown error while seeking content
What it means
isAt() peeks at the stream with fread() and then rewinds with fseek($stream, -$length, SEEK_CUR); a -1 return means the body stream is not seekable and parsing cannot continue. The parser fundamentally requires a seekable body because it re-reads bytes for hash validation, so this surfaces as HTTP 500 (it escapes the plugin's try/catch via isAtLastBoundary()).
Source
Thrown at apps/dav/lib/BulkUpload/MultipartRequestParser.php:106
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);
}
/**
* Check whether the stream's cursor is sitting right before the last boundary.
*/
public function isAtLastBoundary(): bool {
return $this->isAt($this->lastBoundary);
}View on GitHub (pinned to ecdeb153ff)
Solutions
- Buffer the incoming body into a seekable stream (php://temp) before parsing and set it as the request body
- Check which resource the webserver/SAPI hands to PHP for php://input in your setup; standard FPM/Apache setups are seekable, custom servers often are not
- Inspect the stack trace to find which component supplied the non-seekable body and fix or wrap it
Example fix
// before
$parser = new MultipartRequestParser($request, $logger); // body may be non-seekable -> fseek returns -1
// after
$src = $request->getBody();
if (!stream_get_meta_data($src)['seekable']) {
$buf = fopen('php://temp', 'r+b');
stream_copy_to_stream($src, $buf);
rewind($buf);
$request->setBody($buf);
}
$parser = new MultipartRequestParser($request, $logger); Defensive patterns
Strategy: validation
Validate before calling
$body = $request->getBody();
if (!stream_get_meta_data($body)['seekable']) {
$buf = fopen('php://temp', 'r+b');
stream_copy_to_stream($body, $buf);
rewind($buf);
$request->setBody($buf);
} Type guard
function isSeekableStream($stream): bool {
return is_resource($stream) && (stream_get_meta_data($stream)['seekable'] ?? false);
} Prevention
- Always hand Sabre a seekable body resource (php://temp) in custom servers/front controllers
- Check stream_get_meta_data($body)['seekable'] before constructing MultipartRequestParser
- In tests, use fopen('php://memory', 'r+b') fixtures, not pipes
When it happens
Trigger: POST to /dav/bulk where the Sabre\HTTP request body is a non-seekable stream: php://input under SAPI configurations where it is not seekable, a socket/pipe, or a custom stream wrapper without seek support. Also hit when an earlier handler replaces the body with a pipe-like resource.
Common situations: Custom SAPI or CLI servers fronting DAV; stream wrappers that only support sequential access; exotic proxy setups that hand PHP a non-seekable body.
Related errors
- An error occurred while checking content
- Fail to read part's content.
- Boundary not found where it should be.
- An error occurred while reading headers of a part
- 400
AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17).
Data as JSON: /api/errors/49119ea4ebc04ebc.
Report an issue: GitHub.