goharbor/harbor · error · Exception

Trace enabled but no trace exporter set

Error message

Trace enabled but no trace exporter set

What it means

ScanRequest.FromJSON unmarshals a JSON string into the request struct. An empty input string is rejected before json.Unmarshal is invoked, because unmarshalling zero bytes would only produce a confusing syntax error. It is the parse-side guard used when a scan request arrives as a JSON body.

Source

Thrown at make/photon/prepare/models.py:190

            raise Exception('Trace endpoint not set')
        if not self.url_path:
            raise Exception('Trace url path not set')


class Trace:
    def __init__(self, config: dict):
        self.enabled = config.get('enabled') or False
        self.sample_rate = config.get('sample_rate', 1)
        self.namespace = config.get('namespace') or ''
        self.jaeger = JaegerExporter(config.get('jaeger'))
        self.otel = OtelExporter(config.get('otel'))
        self.attributes = config.get('attributes') or {}

    def validate(self):
        if not self.enabled:
            return
        if not self.jaeger.enabled and not self.otel.enabled:
            raise Exception('Trace enabled but no trace exporter set')
        elif self.jaeger.enabled and self.otel.enabled:
            raise Exception('Only can have one trace exporter at a time')
        elif self.jaeger.enabled:
            self.jaeger.validate()
        elif self.otel.enabled:
            self.otel.validate()


class PurgeUpload:
    def __init__(self, config: dict):
        if not config:
            self.enabled = False
        self.enabled = config.get('enabled')
        self.age = config.get('age') or '168h'
        self.interval = config.get('interval') or '24h'
        self.dryrun = config.get('dryrun') or False
        return

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Check that the body is non-empty at the transport layer and reject with 400 there
  2. Propagate body-read errors instead of continuing with an empty string
  3. Log the content length of incoming request bodies to catch stripped payloads early

Example fix

// before
var req v1.ScanRequest
err := req.FromJSON(body) // body may be ""

// after
if body == "" {
    return errors.New("empty scan request body")
}
err := req.FromJSON(body)
Defensive patterns

Strategy: validation

Validate before calling

if body == "" {
    return errors.New("scan request body is empty")
}
var req v1.ScanRequest
return req.FromJSON(body)

Try / catch

var req v1.ScanRequest
if err := req.FromJSON(body); err != nil {
    if strings.Contains(err.Error(), "empty json data to parse") {
        // reject at the HTTP layer with 400 rather than parsing
        return writeBadRequest(w, "empty request body")
    }
    return err
}

Prevention

When it happens

Trigger: req.FromJSON("") — an HTTP handler or queue consumer passed an empty body string, e.g. the request body was empty or the read returned nothing.

Common situations: Empty POST bodies reaching the scan API; message-queue payloads with empty text; proxies or middleware stripping the body; curl calls with a mistyped -d flag.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/6b734d6caee607d3. Report an issue: GitHub.