nextcloud / nextcloud/whiteboard

WhiteboardContentService::getContent() throws JsonException with S3/MinIO objectstore (1-byte empty file stub)

Open Beginner friendly
#1,206 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
215
Forks
39
Avg merge
1d 3h
Merged PRs (30d)
32

Description

Bug description

When using Nextcloud with an S3/MinIO objectstore backend, newly created .whiteboard files are initialized with a 1-byte stub (space character 0x20) instead of an empty file. This causes WhiteboardContentService::getContent() to fail with JsonException, making every whiteboard permanently broken — the canvas appears empty and no content is ever saved.

Steps to reproduce

  1. Install Nextcloud with S3/MinIO objectstore (objectstore class \OC\Files\ObjectStore\S3 in config.php)
  2. Create a new whiteboard file from the Files app
  3. Open the whiteboard

Expected behavior

The whiteboard opens with an empty canvas, the user can draw, and content is saved correctly.

Actual behavior

The whiteboard fails to initialize. The following errors are logged repeatedly in nextcloud.log:

{"app":"whiteboard","message":"Exception handled: JsonException",
 "data":{"file":"WhiteboardContentService.php","line":"37","status_code":"4"}}
{"app":"PHP","message":"Undefined array key 4 at AppFramework/Http.php#106"}

The whiteboard backend WebSocket does receive the connection (visible in backend logs), but since getContent() throws on the GET /apps/whiteboard/{fileId} call before the frontend can initialize, the whiteboard never loads and content is never persisted.

Root cause

getContent() at line ~31 of WhiteboardContentService.php:

public function getContent(File $file): array {
    $fileContent = $file->getContent();
    if ($fileContent === '') {          // ← strict equality misses 1-byte content
        $fileContent = '{"elements":[],"scrollToContent":true}';
    }
    return json_decode($fileContent, true, 512, JSON_THROW_ON_ERROR); // ← throws on " "
}

With the S3/MinIO objectstore, $file->getContent() returns a 1-byte non-empty string (a space 0x20) for newly created files. The === '' check evaluates to false, so json_decode() is called on the 1-byte invalid content → JsonException.

Note: updateContent() already wraps getContent() in a try/catch for exactly this reason. getContent() itself does not.

Proposed fix

public function getContent(File $file): array {
    $fileContent = $file->getContent();
    if ($fileContent === '' || trim($fileContent) === '') {
        $fileContent = '{"elements":[],"scrollToContent":true}';
    }

    try {
        return json_decode($fileContent, true, 512, JSON_THROW_ON_ERROR);
    } catch (\JsonException $e) {
        // Corrupted or 1-byte MinIO stub — return safe default
        return ['elements' => [], 'files' => [], 'scrollToContent' => true];
    }
}

Additional context

A second related bug was found: the whiteboard save endpoint (PUT /apps/whiteboard/{fileId}) must be explicitly whitelisted if a reverse proxy (e.g. HAProxy) restricts HTTP methods — since it is not under /remote.php, /public.php, or /ocs, it is easy to inadvertently block it.

Environment

  • Nextcloud: 33.0.3
  • Whiteboard app + backend: 1.5.8
  • Objectstore: MinIO S3-compatible (4 nodes, erasure coding, MINIO_CI_CD=on in test)
  • Storage strategy: redis (Redis Streams adapter)
  • jwt_expiry default (900s) also triggers auth failures on longer sessions → recommend increasing to 86400

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with WhiteboardContentService.php and its getContent() method, then reproduce the GET /apps/whiteboard/{fileId} request using a newly created file on S3/MinIO storage. Verify handling of the one-byte stub and invalid JSON, with the empty canvas loading and content saving successfully as the completion criteria.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.