{"record":{"id":"fbe317ccecda3b16","repo":"ginuerzh/gost","slug":"empty-chain","errorCode":null,"errorMessage":"empty chain","messagePattern":"empty chain","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chain.go","lineNumber":16,"sourceCode":"package gost\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\nvar (\n\t// ErrEmptyChain is an error that implies the chain is empty.\n\tErrEmptyChain = errors.New(\"empty chain\")\n)\n\n// Chain is a proxy chain that holds a list of proxy node groups.\ntype Chain struct {\n\tisRoute    bool\n\tRetries    int\n\tMark       int\n\tInterface  string\n\tnodeGroups []*NodeGroup\n\troute      []Node // nodes in the selected route\n}\n\n// NewChain creates a proxy chain with a list of proxy nodes.\n// It creates the node groups automatically, one group per node.\nfunc NewChain(nodes ...Node) *Chain {\n\tchain := &Chain{}\n\tfor _, node := range nodes {\n\t\tchain.nodeGroups = append(chain.nodeGroups, NewNodeGroup(node))","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/chain.go#L1-L34","documentation":"ErrEmptyChain is a sentinel error indicating the proxy Chain has no node groups (it is empty). It is returned by Chain.getConn (used by Chain.Conn) when no route is configured, and callers like the SOCKS4 BIND handler explicitly treat it as a benign 'no chain' signal rather than a connection failure.","triggerScenarios":"Calling Chain.Conn() / getConn on a Chain constructed without any nodes, e.g. &Chain{} or a chain whose node groups were never added via Append/Compose, then using it to dial.","commonSituations":"Users configure a gost handler without specifying forward/proxy hops, load a config where the chain section is missing or empty, or build a Chain programmatically and forget to add nodes.","solutions":["Add at least one node/group to the Chain (via AppendNode or the -F/--peer config) before dialing through it","Check Chain.IsEmpty() before calling Conn() and skip chaining when empty","Compare returned error against ErrEmptyChain to treat 'no chain' as direct connection instead of failure","Verify config file actually populates the chain field (typo or empty list)"],"exampleFix":"// before\ncc, err := h.options.Chain.Conn()\nif err != nil {\n    log.Logf(\"dial failed: %s\", err)\n}\n// after\ncc, err := h.options.Chain.Conn()\nif err != nil {\n    if errors.Is(err, ErrEmptyChain) {\n        cc, err = net.Dial(\"tcp\", target) // direct connection, no chain\n    }\n    if err != nil {\n        log.Logf(\"dial failed: %s\", err)\n    }\n}","handlingStrategy":"validation","validationCode":"if chain == nil || chain.IsEmpty() {\n    // no proxy chain configured: dial directly instead of via chain\n    return net.Dial(\"tcp\", addr)\n}\nconn, err := chain.Conn()","typeGuard":"func chainUsable(c *Chain) bool {\n    return c != nil && !c.IsEmpty()\n}","tryCatchPattern":"conn, err := chain.Conn()\nif err != nil {\n    if errors.Is(err, ErrEmptyChain) {\n        // handle: no chain configured\n        return fallbackDial(addr)\n    }\n    return err\n}","preventionTips":["Always populate the chain in config before enabling chain-based handlers","Check IsEmpty() before Conn() when chains are optional in your app","Compare against the exported ErrEmptyChain sentinel rather than string matching","Log chain configuration at startup to catch empty-chain mistakes early"],"tags":["proxy-chain","go","configuration","sentinel-error"],"backgroundTag":"empty-proxy-chain","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}