{"record":{"id":"f5c42b55c249aebf","repo":"Billionmail/BillionMail","slug":"base-url-not-configured","errorCode":null,"errorMessage":"base URL not configured","messagePattern":"base URL not configured","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/campaign/campaign_v1_form.go","lineNumber":99,"sourceCode":"\tif err = r.Session.Set(\"csrf_token\", csrfToken); err != nil {\n\t\tcsrfToken = \"\"\n\t}\n\n\tgroups, _ := contact.GetAllGroups(ctx, \"\")\n\n\terr = r.Response.WriteTpl(\"subscription_form.html\", g.Map{\n\t\t\"Groups\":    groups,\n\t\t\"CSRFToken\": csrfToken,\n\t})\n\treturn\n}\n\n// sendConfirmationEmail sends a confirmation email to the newly subscribed user\nfunc sendConfirmationEmail(ctx context.Context, email, name string) error {\n\t// Get the base domain to construct noreply email\n\tbaseURL := domains.GetBaseURL()\n\tif baseURL == \"\" {\n\t\treturn fmt.Errorf(\"base URL not configured\")\n\t}\n\n\t// Extract domain from base URL\n\tdomain := \"\"\n\tif u, err := url.Parse(baseURL); err == nil && u.Hostname() != \"\" {\n\t\tdomain = u.Hostname()\n\t} else {\n\t\t// Fallback: try to get from environment\n\t\tif hostname, err := public.DockerEnv(\"BILLIONMAIL_HOSTNAME\"); err == nil && hostname != \"\" {\n\t\t\tdomain = hostname\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"unable to determine domain for noreply email\")\n\t\t}\n\t}\n\n\t// Construct noreply email address\n\tnoreplyEmail := fmt.Sprintf(\"noreply@%s\", domain)\n","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/campaign/campaign_v1_form.go#L81-L117","documentation":"HttpGetSrc in public/common.go performs an HTTP GET and, for any status code outside 2xx (or <200), records an error formatted as 'http get error: %s' with resp.Status (e.g. '404 Not Found'). Note the function still proceeds to read the body; the error is returned with whatever body was read, so callers get a non-2xx response surfaced as a Go error.","triggerScenarios":"Any call through HttpGetSrc (directly or via HttpRequestTool, RequestUrl, HttpGetJson) where the remote server returns 1xx, 3xx (unfollowed redirects), 4xx or 5xx — dead URL, auth failure, server error, redirect to a non-followed location.","commonSituations":"Calling an internal API with an expired token (401/403), fetching a moved endpoint (404), a downstream service being down (502/503), or a server returning 3xx with redirects disabled.","solutions":["Inspect resp.Status in the error to identify the concrete HTTP status and address it (fix URL, auth, or server)","Verify the target URL is reachable (curl the endpoint) and correct any typos or port errors","Add/refresh authentication credentials if the status is 401/403","Enable redirect following or call the final URL directly for 3xx statuses; add retry/backoff for 5xx"],"exampleFix":"// before\nbody, err := public.HttpGetSrc(ctx, client, \"http://host/old-endpoint\")\n// after\nif body, err = public.HttpGetSrc(ctx, client, \"https://host/new-endpoint\"); err != nil {\n\tg.Log().Errorf(ctx, \"GET failed: %v\", err)\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\nif resp, err := client.Do(req); err == nil {\n\tresp.Body.Close()\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"endpoint unhealthy: %s\", resp.Status)\n\t}\n}","typeGuard":null,"tryCatchPattern":"body, err := public.HttpGetSrc(ctx, client, url)\nif err != nil {\n\tvar he *fmt.Errorf // inspect status embedded in message\n\tg.Log().Warningf(ctx, \"GET %s failed: %v\", url, err)\n\tif strings.Contains(err.Error(), \"50\") { // 5xx: retry with backoff\n\t\treturn retryWithBackoff(url)\n\t}\n\treturn err\n}","preventionTips":["Check the target URL and credentials before automated calls","Treat 4xx and 5xx differently: fix inputs for 4xx, retry with backoff for 5xx","Set timeouts on the HTTP client so hangs fail fast","Monitor upstream endpoints for moves (301/404) and update callers"],"tags":["http","network","request","status-code"],"backgroundTag":"http-non-2xx-response","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}