Kaleido 1.0.0 errors within Docker container

Kaleido._kaleido_tab.JavascriptError is encountered when running Plotly in a docker container
Stack trace indicates this is emanating from:

File “/usr/local/lib/python3.12/site-packages/kaleido/_kaleido_tab.py”, line 323, in _calc_fig
raise e

Environment:

  • Python 3.12.0
  • Plotly 6.1.1
  • kaleido 1.0.0
  • Acquiring stable Chrome while building the container (this is a MUST, our end-users do not want to access Chrome during run-time- restrictions on Internet use)
  • Verified google-chrome and google-chrome-stable are in the /usr/bin directory of container

The plotting is successful when running the code OUTSIDE the container, so this indicates something(s) was/were overlooked or missing in setting up the container??? Any suggestions for trouble-shooting this issue would be appreciated.


Full stack trace:
File “/METviewer-python/METplotpy/metplotpy/plots/base_plot.py”, line 376, in save_to_file
self.figure.write_image(image_name)
File “/usr/local/lib/python3.12/site-packages/plotly/basedatatypes.py”, line 3911, in write_image
return pio.write_image(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/plotly/io/_kaleido.py”, line 509, in write_image
img_data = to_image(
^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/plotly/io/_kaleido.py”, line 373, in to_image
img_bytes = kaleido.calc_fig_sync(
^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/kaleido/init.py”, line 145, in calc_fig_sync
return _async_thread_run(calc_fig, args=args, kwargs=kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/kaleido/init.py”, line 138, in _async_thread_run
raise res
File “/usr/local/lib/python3.12/site-packages/kaleido/init.py”, line 129, in run
q.put(asyncio.run(func(*args, **kwargs)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/asyncio/runners.py”, line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/asyncio/runners.py”, line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/asyncio/base_events.py”, line 664, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/kaleido/init.py”, line 55, in calc_fig
return await k.calc_fig(
^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/kaleido/kaleido.py”, line 332, in calc_fig
data = await asyncio.wait_for(
^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/asyncio/tasks.py”, line 510, in wait_for
return await fut
^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/kaleido/_kaleido_tab.py”, line 323, in _calc_fig
raise e
kaleido._kaleido_tab.JavascriptError: {‘result’: {‘type’: ‘object’, ‘subtype’: ‘error’, ‘className’: ‘ReferenceError’, ‘description’: ‘ReferenceError: Plotly is not defined\n at Object.render [as plotly] (file:///usr/local/lib/python3.12/site-packages/kaleido/vendor/kaleido_scopes.js:3021:18)\n at :1:48’, ‘objectId’: ‘-5345872899737574680.1.2’}, ‘exceptionDetails’: {‘exceptionId’: 2, ‘text’: ‘Uncaught’, ‘lineNumber’: 3020, ‘columnNumber’: 17, ‘scriptId’: ‘8’, ‘stackTrace’: {‘callFrames’: [{‘functionName’: ‘render’, ‘scriptId’: ‘8’, ‘url’: ‘file:///usr/local/lib/python3.12/site-packages/kaleido/vendor/kaleido_scopes.js’, ‘lineNumber’: 3020, ‘columnNumber’: 17}, {‘functionName’: ‘’, ‘scriptId’: ‘12’, ‘url’: ‘’, ‘lineNumber’: 0, ‘columnNumber’: 47}]}, ‘exception’: {‘type’: ‘object’, ‘subtype’: ‘error’, ‘className’: ‘ReferenceError’, ‘description’: ‘ReferenceError: Plotly is not defined\n at Object.render [as plotly] (file:///usr/local/lib/python3.12/site-packages/kaleido/vendor/kaleido_scopes.js:3021:18)\n at :1:48’, ‘objectId’: ‘-5345872899737574680.1.3’}}}
root@045c5ccf62b5:/tmp#

Hey @mwin I can’t reproduce this. My friend Claude helped me with that.

Dockerfile:

FROM python:3.12-slim

# Set environment variables
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1

# Update and install system dependencies
RUN apt-get update && apt-get install -y \
    python3-pip \
    wget \
    curl \
    gnupg \
    software-properties-common \
    xvfb \
    && rm -rf /var/lib/apt/lists/*


# Install Google Chrome
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
    && apt-get update \
    && apt-get install -y google-chrome-stable \
    && rm -rf /var/lib/apt/lists/*

# Verify Chrome installation
RUN ls -la /usr/bin/google-chrome* && \
    google-chrome --version

# Upgrade pip
RUN python3 -m pip install --upgrade pip

# Install Python packages
RUN pip install plotly==6.1.1 kaleido==1.0.0

# Create working directory
WORKDIR /app

# Copy application files
COPY main.py .

# Create output directory
RUN mkdir -p /app/output

# Run the application
CMD ["python", "main.py"]

docker-compose.yaml

services:
  plotly-app:
    build: .
    container_name: plotly-chart-generator
    volumes:
      - ./output:/app/output
    environment:
      - DISPLAY=:99
    command: >
      bash -c "
        Xvfb :99 -screen 0 1024x768x24 &
        python main.py
      "
    restart: "no"

main.py:

import plotly.graph_objects as go
import plotly.io as pio
import random
import os

def generate_random_line_chart():
    """Generate a random line chart using plotly and save as PNG"""
    
    # Generate random data
    x_values = list(range(1, 21))  # 20 data points
    y_values = [random.randint(10, 100) for _ in range(20)]
    
    # Create the line chart
    fig = go.Figure()
    
    fig.add_trace(go.Scatter(
        x=x_values,
        y=y_values,
        mode='lines+markers',
        name='Random Data',
        line=dict(color='#1f77b4', width=3),
        marker=dict(size=8)
    ))
    
    # Update layout
    fig.update_layout(
        title='Random Line Chart',
        xaxis_title='X Values',
        yaxis_title='Y Values',
        font=dict(size=12),
        width=800,
        height=600,
        template='plotly_white'
    )
    
    # Ensure output directory exists
    os.makedirs('/app/output', exist_ok=True)
    
    # Save as PNG
    output_path = '/app/output/random_line_chart.png'
    fig.write_image(output_path)
    
    print(f"Chart saved successfully to {output_path}")
    print(f"Data points: {len(x_values)}")
    print(f"Y-value range: {min(y_values)} to {max(y_values)}")

if __name__ == "__main__":
    generate_random_line_chart()

Unfortunately, this Dockerfile does not build successfully due to an inability to locate google-chrome-stable.

Works on my end. There is no way we can help you any further without knowing exactly what you are doing.

  • A bit more complicated than the typical plotting paradigm. We have a few ways to generate plots: directly via command line, through use case wrapper scripts, and via Java web app which invokes the Python plotting scripts. The Java web app can be installed on a host, or run in a container.

  • The web app successfully generates plots when installed on a host. The plots are also successfully generated via command line.

  • When the web app is deployed into a container, we observe javascript error observed from _kaleido_tab.py (_calc_fig).

  • Executing the /bin/bash script inside the container and running plotting via command line generates the same javascript error, but provides the stack trace.

Our Dockerfile is a bit more complex, we build on MacOS with the following command:

docker build --platform linux/amd64 -t metviewer .

Dockerfile

ARG MET_BASE_REPO=met-base-metviewer
ARG MET_BASE_TAG=v3.4

FROM dtcenter/${MET_BASE_REPO}:${MET_BASE_TAG}
LABEL org.opencontainers.image.authors="John Halley Gotway <johnhg@ucar.edu>"


#
# This Dockerfile checks out METviewer and its dependencies from GitHub and builds
# the specified branch or tag. Use the develop branches for dependencies by default
# but override with "--build-arg".
#

ARG METPLOTPY_GIT_NAME=feature_525_update_plotly_kaleido
ARG METCALCPY_GIT_NAME=develop
ARG METDATAIO_GIT_NAME=develop

#
# METVIEWER_GIT_NAME is required.
ARG METVIEWER_GIT_NAME=bugfix_kaleido_docker
#
RUN if [ "x${METVIEWER_GIT_NAME}" = "x" ]; then \
      echo "ERROR: METVIEWER_GIT_NAME undefined! Rebuild with \"--build-arg METVIEWER_GIT_NAME={branch, tag, or hash}\""; \
      exit 1; \
    fi 

ENV METVIEWER_GIT_URL=https://github.com/dtcenter/METviewer
ENV CATALINA_HOME=/opt/tomcat

RUN echo "Build Argument METVIEWER_GIT_NAME=${METVIEWER_GIT_NAME}" \
 && echo "Build Argument METPLOTPY_GIT_NAME=${METPLOTPY_GIT_NAME}" \
 && echo "Build Argument METCALCPY_GIT_NAME=${METCALCPY_GIT_NAME}" \
 && echo "Build Argument METDATAIO_GIT_NAME=${METDATAIO_GIT_NAME}"

#
# Set env vars
#
ENV PYTHONPATH="/METviewer-python/METcalcpy/:/METviewer-python/METplotpy/"
ENV METPLOTPY_BASE="/METviewer-python/METplotpy/"

#
# Update the OS, as needed
#
RUN apt update && apt -y upgrade

#
# Clone the METviewer repository
#
RUN echo "Checking out METviewer ${METVIEWER_GIT_NAME} from ${METVIEWER_GIT_URL}"
RUN git clone --branch ${METVIEWER_GIT_NAME} ${METVIEWER_GIT_URL} /METviewer

#
# Configure METviewer
#
RUN echo "Configuring METviewer" \
 && cd /METviewer \
 && cat webapp/metviewer/WEB-INF/classes/build.properties | \
    sed -r 's%db.host=.*%db.host=mysql_mv%g' | \
    sed -r 's%db.user=.*%db.user=root%g' | \
    sed -r 's%db.password=.*%db.password=mvuser%g' | \
    sed -r 's%db.management.system=.*%db.management.system=mysql%g' | \
    sed -r 's%output.dir=.*%output.dir=/opt/tomcat/webapps/metviewer_output/%g' | \
    sed -r 's%webapps.dir=.*%webapps.dir=/opt/tomcat/webapps/metviewer/%g' | \
    sed -r 's%url.output=.*%url.output=http://localhost:8080/metviewer_output/%g' | \
    sed -r 's%python.env=.*%python.env=/usr/local%g' | \
    sed -r 's%metcalcpy.home=.*%metcalcpy.home=/METviewer-python/METcalcpy/%g' | \
    sed -r 's%metplotpy.home=.*%metplotpy.home=/METviewer-python/METplotpy/%g' \
    > build.properties

#
# Run the build script
#
RUN cd /METviewer \
 && internal/scripts/docker/build_metviewer_docker.sh

# Explicitly specify the shell to indicate that using the SHELL
# instruction is a conscious decision: 
# https://docs.docker.com/reference/build-checks/json-args-recommended/
SHELL ["/bin/sh", "-c"]
ENTRYPOINT ${CATALINA_HOME}/bin/startup.sh && /bin/bash
CMD ["true"]


#
# Get Chrome
#
# install wget
RUN apt -f install -y
RUN apt update
RUN apt install -y wget

# Install Chrome
RUN wget -q  https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN apt install -y ./google-chrome-stable_current_amd64.deb

RUN ls -la /usr/bin/google-chrome* && \
      google-chrome --version

# Set the PRE_LOAD_CHROME env variable to True to prevent invoking get_chrome_sync() from METplotpy 
ENV PRE_LOAD_CHROME='False'
RUN echo "INFO: environment PRE_LOAD_CHROME set to ${PRE_LOAD_CHROME} ";


# Ensure correct Plotly and kaleido are installed
# based on requirements.txt file
RUN pip install PLOTLY==6.1.1
RUN pip install kaleido==1.0.0
COPY . .

docker-compose.yml

services:
  db:
      image: mariadb:10.5.8
      container_name: mysql_mv
      user: "mysql:mysql"
      ports:
          - "6603:3306"
      volumes:
          - ${MYSQL_DIR}:/var/lib/mysql

      environment:
          MYSQL_ROOT_PASSWORD: mvuser
      tty: true
  #
  metviewer:
      image: ${METVIEWER_DOCKER_IMAGE}
      container_name: metviewer_1
      ports:
      - "8080:8080"
      volumes:
        - ${METVIEWER_DATA}:/data
        - ${METVIEWER_DIR}/metviewer_output/xml:/opt/tomcat/webapps/metviewer_output/xml
        - ${METVIEWER_DIR}/metviewer_output/plots:/opt/tomcat/webapps/metviewer_output/plots
        - ${METVIEWER_DIR}/metviewer_output/data:/opt/tomcat/webapps/metviewer_output/data
        - ${METVIEWER_DIR}/metviewer_output/scripts:/opt/tomcat/webapps/metviewer_output/scripts
      links:
        - db:mysql_mv
      tty: true

Thank you for your assistance.

I managed to reproduce my error message using your Dockerfile and docker-compose.yml:

because I am using an M2 chip on my Mac, I built this way:

docker build --platform linux/amd64 -t .

And I modified the docker-compose.yml by adding the following:
platform: linux/amd64

Upon inspection of my container log (via Docker Desktop), I can replicate the same error I observe in my plotting (via Java Web app and command line). So perhaps this is an issue with amd64 running on arm???

truncated error message:

ile “/usr/local/lib/python3.12/site-packages/kaleido/kaleido.py”, line 332, in calc_fig

data = await asyncio.wait_for(

^^^^^^^^^^^^^^^^^^^^^^^

File “/usr/local/lib/python3.12/asyncio/tasks.py”, line 520, in wait_for

return await fut

^^^^^^^^^

File “/usr/local/lib/python3.12/site-packages/kaleido/_kaleido_tab.py”, line 323, in _calc_fig

raise e

kaleido._kaleido_tab.JavascriptError: {‘result’: {‘type’: ‘object’, ‘subtype’: ‘error’, ‘className’: ‘ReferenceError’, ‘description’: ‘ReferenceError: Plotly is not defined\n at Object.render [as plotly] (file:///usr/local/lib/python3.12/site-packages/kaleido/vendor/kaleido_scopes.js:3021:18)\n at :1:48’, ‘objectId’: ‘-2948903285713425727.1.2’}, ‘exceptionDetails’: {‘exceptionId’: 2, ‘text’: ‘Uncaught’, ‘lineNumber’: 3020, ‘columnNumber’: 17, ‘scriptId’: ‘8’, 'stac

I actually can’t give you an answer. Let my try to ask around.

@AIMPED thank you for supporting @mwin,

@mwin , this is a question I have to ask my colleagues. We’ll get back from you as soon as we have a good answer.

1 Like

Thank you for all your assistance. I borrowed an older Mac with an Intel chip and I can successfully generate plots when running in a Docker container. So it appears that the errors emanating from _kaleido_tab.py are linked to running on Macs with M2 chipset. In the meantime, our IT is setting up Docker on linux and I will test in that environment, hopefully the error does not occur in that environment.

2 Likes