HelloZeroNet/ZeroNet · warning · Exception

Only SELECT query supported

Error message

Only SELECT query supported

What it means

actionChartDbQuery is the ZeroNet chart API exposed to (untrusted) chart providers. To prevent SQL injection and data modification, it only permits read-only SELECT statements and rejects anything else before executing. The rejection is also returned to the client as {'error': ...} rather than raised over RPC.

Source

Thrown at plugins/Chart/ChartPlugin.py:39

    def load(self, *args, **kwargs):
        back = super(SiteManagerPlugin, self).load(*args, **kwargs)
        collector.setInitialLastValues(self.sites.values())
        return back

    def delete(self, address, *args, **kwargs):
        db.deleteSite(address)
        return super(SiteManagerPlugin, self).delete(address, *args, **kwargs)

@PluginManager.registerTo("UiWebsocket")
class UiWebsocketPlugin(object):
    @flag.admin
    def actionChartDbQuery(self, to, query, params=None):
        if config.debug or config.verbose:
            s = time.time()
        rows = []
        try:
            if not query.strip().upper().startswith("SELECT"):
                raise Exception("Only SELECT query supported")
            res = db.execute(query, params)
        except Exception as err:  # Response the error to client
            self.log.error("ChartDbQuery error: %s" % err)
            return {"error": str(err)}
        # Convert result to dict
        for row in res:
            rows.append(dict(row))
        if config.verbose and time.time() - s > 0.1:  # Log slow query
            self.log.debug("Slow query: %s (%.3fs)" % (query, time.time() - s))
        return rows

    @flag.admin
    def actionChartGetPeerLocations(self, to):
        peers = {}
        for site in self.server.sites.values():
            peers.update(site.peers)
        peer_locations = self.getPeerLocations(peers)
        return peer_locations

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Rewrite the query to start with the SELECT keyword (no leading comments/CTEs before it)
  2. Perform writes/schema changes in trusted site code (dbschema.json / site owner context), never via the chart API
  3. If you need only a count or specific fields, keep it a plain 'SELECT ...' query
  4. Note the error is returned as {'error': ...} in the response — check the response's error key in your chart provider code

Example fix

// before
query = "-- daily stats\nSELECT * FROM message"  // leading comment fails
// after
query = "SELECT * FROM message"
Defensive patterns

Strategy: validation

Validate before calling

query = query.strip()
if not query.upper().startswith('SELECT'):
    raise ValueError('ChartDbQuery only accepts SELECT queries')

Try / catch

try:
    res = await cmd('chartDbQuery', query)
except Exception as err:
    if 'Only SELECT query supported' in (res.get('error') or str(err)):
        query = rewrite_as_select(query)
        retry_with_select_only()

Prevention

When it happens

Trigger: Calling the ChartDbQuery API (user_action chartDbQuery) with a query that does not start with SELECT — e.g. INSERT/UPDATE/DELETE/DROP, a leading comment or whitespace like '-- dump\nSELECT ...', or queries beginning with WITH/EXPLAIN/PRAGMA.

Common situations: Chart provider code accidentally sending write queries; queries built with leading SQL comments; attempting schema setup (CREATE TABLE) through the chart API instead of the site's own trusted context; case/format issues defeated by .strip().upper().

Related errors


AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02). Data as JSON: /api/errors/ae65f74fa88be13d. Report an issue: GitHub.