{"record":{"id":"555fb5e3e2af167d","repo":"AlexxIT/go2rtc","slug":"hap-can-t-dial-witout-client-id-or-client-private","errorCode":null,"errorMessage":"hap: can't dial witout client_id or client_private","messagePattern":"hap: can't dial witout client_id or client_private","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hap/client.go","lineNumber":90,"sourceCode":"}\n\nfunc (c *Client) URL() string {\n\treturn fmt.Sprintf(\n\t\t\"homekit://%s?device_id=%s&device_public=%16x&client_id=%s&client_private=%32x\",\n\t\tc.DeviceAddress, c.DeviceID, c.DevicePublic, c.ClientID, c.ClientPrivate,\n\t)\n}\n\nfunc (c *Client) DeviceHost() string {\n\tif i := strings.IndexByte(c.DeviceAddress, ':'); i > 0 {\n\t\treturn c.DeviceAddress[:i]\n\t}\n\treturn c.DeviceAddress\n}\n\nfunc (c *Client) Dial() (err error) {\n\tif len(c.ClientID) == 0 || len(c.ClientPrivate) == 0 {\n\t\treturn errors.New(\"hap: can't dial witout client_id or client_private\")\n\t}\n\n\t// update device address (host and/or port) before dial\n\t_ = mdns.QueryOrDiscovery(c.DeviceHost(), mdns.ServiceHAP, func(entry *mdns.ServiceEntry) bool {\n\t\tif entry.Complete() && entry.Info[\"id\"] == c.DeviceID {\n\t\t\tc.DeviceAddress = entry.Addr()\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t})\n\n\t// TODO: close conn on error\n\tif c.Conn, err = net.DialTimeout(\"tcp\", c.DeviceAddress, ConnDialTimeout); err != nil {\n\t\treturn\n\t}\n\n\tc.reader = bufio.NewReader(c.Conn)\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/AlexxIT/go2rtc/blob/c245815e75e2a5fd60b4290f12bfc04e55a984d3/pkg/hap/client.go#L72-L108","documentation":"Client.Dial refuses to start HAP session establishment when the client's pairing identity is not configured: either ClientID or ClientPrivate (the client's ed25519 ID and private key from a previous pairing) is empty. Dial needs these to sign the session and authenticate to the accessory.","triggerScenarios":"Constructing a Client with only device fields (DeviceID/DeviceAddress) and forgetting to load the client identity; loading persisted pairing state from a config file that is missing the client_id/client_private fields; creating a fresh Client for a device that was already paired elsewhere.","commonSituations":"Config file missing the client identity keys after a fresh install or migration; JSON unmarshalling silently leaving the fields empty due to wrong key names; running the client on a new host without copying the pairing credentials.","solutions":["Set ClientID and ClientPrivate before calling Dial — load them from persisted pairing state or pair the device first.","Check the config file/struct mapping: the JSON keys client_id and client_private must actually populate the fields.","If no pairing exists yet, run the Pair flow first to establish the client identity, then persist it.","Log the lengths of both fields before Dial to confirm they are non-empty at runtime."],"exampleFix":"// before: client identity missing -> Dial fails\nclient := &hap.Client{DeviceID: deviceID, DeviceAddress: addr}\nerr := client.Dial()\n// after: load persisted identity first\nclient := &hap.Client{\n    DeviceID: deviceID, DeviceAddress: addr,\n    ClientID:      cfg.HAP.ClientID,       // non-empty\n    ClientPrivate: cfg.HAP.ClientPrivate,  // non-empty ed25519 private key\n}\nif len(client.ClientID) == 0 || len(client.ClientPrivate) == 0 {\n    return errors.New(\"pair the device first; client identity missing\")\n}\nerr := client.Dial()","handlingStrategy":"validation","validationCode":"if len(client.ClientID) == 0 || len(client.ClientPrivate) == 0 {\n    return errors.New(\"client identity missing: pair the device first and persist client_id/client_private\")\n}","typeGuard":null,"tryCatchPattern":"if err := client.Dial(); err != nil {\n    if strings.Contains(err.Error(), \"witout client_id\") {\n        return fmt.Errorf(\"configuration error: run the pairing setup to create the client identity: %w\", err)\n    }\n    return err\n}","preventionTips":["Persist ClientID/ClientPrivate immediately after the first successful Pair and reload them at startup.","Validate the pairing config struct (both fields non-empty) before constructing the Client.","Watch for JSON key mismatches (client_id vs ClientID) that silently leave fields empty."],"tags":["hap","credentials","configuration","pairing"],"backgroundTag":"missing-credentials","analyzedSha":"c245815e75e2a5fd60b4290f12bfc04e55a984d3","analyzedAt":"2026-09-07T11:47:02.965Z","contentChangedAt":"2026-09-07T11:47:02.965Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}