IceWhaleTech/CasaOS · error

err.Error()

Error message

err.Error()

What it means

In ZerotierProxy (route/v1/zerotier.go) this is the raw error surfaced when ioutil.ReadFile fails to read /var/lib/zerotier-one/zerotier-one.port, the file in which a running ZeroTier service publishes its local control-API port. http.Error() writes the message to the client, but the code does NOT return, so execution continues with a nil/empty port and produces a bogus target URL — the real defect this error reveals is the missing early return.

Source

Thrown at route/v1/zerotier.go:26

	"net/http"
	"strings"
	"time"

	"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
	"github.com/IceWhaleTech/CasaOS/common"
	"github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
	"github.com/labstack/echo/v4"
	"github.com/tidwall/gjson"
	"go.uber.org/zap"
)

func ZerotierProxy(ctx echo.Context) error {
	// Read the port number from the file
	w := ctx.Response().Writer
	r := ctx.Request()
	port, err := ioutil.ReadFile("/var/lib/zerotier-one/zerotier-one.port")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	// Get the request path and remove "/zt"
	path := strings.TrimPrefix(r.URL.Path, "/v1/zt")
	fmt.Println(path)

	// Build the target URL
	targetURL := fmt.Sprintf("http://localhost:%s%s", strings.TrimSpace(string(port)), path)

	// Create a new request
	req, err := http.NewRequest(r.Method, targetURL, r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	// Add the X-ZT1-AUTH header
	authToken, err := ioutil.ReadFile("/var/lib/zerotier-one/authtoken.secret")
	if err != nil {

View on GitHub (pinned to 0d3b2f444e)

Solutions

  1. Confirm zerotier-one is running (zerotier-cli info / systemctl status zerotier-one).
  2. Verify the file exists and is readable: ls -l /var/lib/zerotier-one/zerotier-one.port.
  3. Add `return` immediately after the http.Error call so a failed read does not cascade into an invalid proxy request.
  4. When proxying from a container, bind-mount the host's /var/lib/zerotier-one into it.

Example fix

// before
port, err := ioutil.ReadFile("/var/lib/zerotier-one/zerotier-one.port")
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
}

// after
port, err := os.ReadFile("/var/lib/zerotier-one/zerotier-one.port")
if err != nil {
    http.Error(w, "zerotier service not available: "+err.Error(), http.StatusInternalServerError)
    return
}
Defensive patterns

Strategy: validation

Validate before calling

// before proxying any /v1/zt request
portBytes, err := os.ReadFile("/var/lib/zerotier-one/zerotier-one.port")
if err != nil {
    // service down: fail fast with a clean message instead of proxying
    return ctx.String(http.StatusServiceUnavailable, "zerotier-one service is not running")
}
port := strings.TrimSpace(string(portBytes))
if _, err := strconv.Atoi(port); err != nil {
    return ctx.String(http.StatusServiceUnavailable, "zerotier port file invalid")
}

Prevention

When it happens

Trigger: GET/POST any /v1/zt/* route when (1) zerotier-one is not installed, (2) the service is stopped so the port file was never created/removed, (3) CasaOS runs as a user without read permission on /var/lib/zerotier-one, (4) ZeroTier version predates the port-file mechanism.

Common situations: Zerotier add-on removed but UI still issues proxy calls; fresh install where the user opened the ZT panel before starting the service; containerized CasaOS where /var/lib/zerotier-one is not bind-mounted from the host; permission hardening (chmod 700) on the zerotier state dir.

Related errors


AI-assisted analysis of IceWhaleTech/CasaOS@0d3b2f444e (2026-08-15). Data as JSON: /api/errors/326123eb4a772ac9. Report an issue: GitHub.