louislam/uptime-kuma · warning · Error

Only allowed PNG logo.

Error message

Only allowed PNG logo.

What it means

Thrown by saveStatusPage when imgDataUrl starts with 'data:' but does not start with the PNG data-URL header 'data:image/png;base64,'. Only PNG logos are accepted because the saved file is hard-coded to logo<id>.png via ImageDataURI.outputFile.

Source

Thrown at server/socket-handlers/status-page-socket-handler.js:312

            checkLogin(socket);

            // Save Config
            let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);

            if (!statusPage) {
                throw new Error("No slug?");
            }

            checkSlug(config.slug);

            const header = "data:image/png;base64,";

            // Check logo format
            // If is image data url, convert to png file
            // Else assume it is a url, nothing to do
            if (imgDataUrl.startsWith("data:")) {
                if (!imgDataUrl.startsWith(header)) {
                    throw new Error("Only allowed PNG logo.");
                }

                const filename = `logo${statusPage.id}.png`;

                // Convert to file
                await ImageDataURI.outputFile(imgDataUrl, Database.uploadDir + filename);
                config.logo = `/upload/${filename}?t=` + Date.now();
            } else {
                config.logo = imgDataUrl;
            }

            statusPage.slug = config.slug;
            statusPage.title = config.title;
            statusPage.description = config.description;
            statusPage.icon = config.logo;
            ((statusPage.autoRefreshInterval = config.autoRefreshInterval), (statusPage.theme = config.theme));
            //statusPage.published = ;
            //statusPage.search_engine_index = ;

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Convert the logo to PNG before sending (canvas toBlob('image/png') or an image tool).
  2. If the image is hosted externally, pass a URL instead of a data: URL (the else branch treats it as a URL).
  3. Reject non-PNG files in the client file picker before encoding to data URL.

Example fix

// before
socket.emit("saveStatusPage", slug, config, "data:image/jpeg;base64,...", groups, cb);
// after
socket.emit("saveStatusPage", slug, config, "data:image/png;base64,...", groups, cb);
Defensive patterns

Strategy: validation

Validate before calling

const PNG_HEADER = "data:image/png;base64,";
function encodeAsPng(file) { /* use canvas.toBlob('image/png') */ }
if (imgDataUrl.startsWith("data:") && !imgDataUrl.startsWith(PNG_HEADER)) {
  imgDataUrl = await encodeAsPng(file);
}
socket.emit("saveStatusPage", slug, config, imgDataUrl, groups, cb);

Type guard

function isPngDataUrl(s) {
  return typeof s === "string" && s.startsWith("data:image/png;base64,");
}

Try / catch

try { await emitAsync("saveStatusPage", slug, config, img, groups, cb); }
catch (e) { if (/Only allowed PNG/.test(e.message)) convertAndRetry(); else throw e; }

Prevention

When it happens

Trigger: Client uploads a JPEG, GIF, SVG, or WebP image as a data URL for the status page logo; the data: prefix is present but the MIME is not image/png.

Common situations: User pastes/uploads a non-PNG logo; frontend does not pre-convert; browser produces image/jpeg for screenshots; SVG rejected for security.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/5687616419ebfbf3. Report an issue: GitHub.