MHSanaei/3x-ui · error

decode web cert files: %w

Error message

decode web cert files: %w

What it means

Returned by Remote.GetWebCertFiles when the node's panel/api/server/getWebCertFiles endpoint answered 200 but env.Obj does not decode into WebCertFiles (the expected cert/key path strings). The node-side handler exists (otherwise error 101/102 would fire), but the payload shape does not match.

Source

Thrown at internal/web/runtime/remote.go:648

// WebCertFiles holds a node's own web TLS certificate and key file paths.
type WebCertFiles struct {
	WebCertFile string `json:"webCertFile"`
	WebKeyFile  string `json:"webKeyFile"`
}

// GetWebCertFiles fetches the node's own web TLS certificate/key file paths so
// the central panel can offer them as the "Set Cert from Panel" default for a
// node-assigned inbound — those paths exist on the node, the central panel's
// don't. See issue #4854.
func (r *Remote) GetWebCertFiles(ctx context.Context) (*WebCertFiles, error) {
	env, err := r.do(ctx, http.MethodGet, "panel/api/server/getWebCertFiles", nil)
	if err != nil {
		return nil, err
	}
	var files WebCertFiles
	if err := json.Unmarshal(env.Obj, &files); err != nil {
		return nil, fmt.Errorf("decode web cert files: %w", err)
	}
	return &files, nil
}

// GetDescendants fetches the node's read-only summaries of the nodes IT
// manages, so this panel can surface them as transitive sub-nodes in a chained
// topology (#4983). Best-effort: an old-build node without the endpoint returns
// an error the caller ignores.
func (r *Remote) GetDescendants(ctx context.Context) ([]model.NodeSummary, error) {
	env, err := r.do(ctx, http.MethodGet, "panel/api/server/descendants", nil)
	if err != nil {
		return nil, err
	}
	var out []model.NodeSummary
	if len(env.Obj) > 0 {
		if err := json.Unmarshal(env.Obj, &out); err != nil {
			return nil, fmt.Errorf("decode descendants: %w", err)
		}

View on GitHub (pinned to ad32144c42)

Solutions

  1. Upgrade the node to the same release as the master — this endpoint is version-coupled (issue #4854 feature).
  2. curl the endpoint on the node and compare obj's keys with WebCertFiles fields.
  3. If the node has no cert configured, set the node's own web cert first so the handler returns real paths.
  4. As a workaround, enter the cert paths manually instead of using the panel-default fetch.

Example fix

// before: old node returns {"obj": null} when no cert is set
// err: decode web cert files: json: cannot unmarshal null into Go value of type runtime.WebCertFiles

// after: upgrade node / configure the node's web cert so obj carries the path strings
Defensive patterns

Strategy: fallback

Validate before calling

// Version/feature gate before offering the panel-default cert button
if !nodeSupportsWebCertFiles(n) {
    ui.DisableSetCertFromPanel(n.ID) // fall back to manual path entry
}

Type guard

func isCertDecodeError(err error) bool {
    return err != nil && strings.HasPrefix(err.Error(), "decode web cert files:")
}

Try / catch

files, err := nodeSvc.GetWebCertFiles(id)
if err != nil {
    if isCertDecodeError(err) {
        files = nil // degrade to manual cert path input in the UI
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Invoking 'Set Cert from Panel' for a node-assigned inbound against a node build whose getWebCertFiles returns a different obj shape, obj:null, or an error object inside a success envelope; node version predates the field layout the master expects.

Common situations: Master newer than node (endpoint added recently, layout drifted); node where web cert paths are unset so the handler returns obj with nulls on older builds.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/cc76de80225d5fee. Report an issue: GitHub.