{"record":{"id":"1be867fc8f706051","repo":"sipeed/picoclaw","slug":"api-key-is-required","errorCode":null,"errorMessage":"api key is required","messagePattern":"api key is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/model/online.go","lineNumber":29,"sourceCode":"\ntype modelEntry struct {\n\tID          string `json:\"id\"`\n\tName        string `json:\"name\"`\n\tDescription string `json:\"description\"`\n}\n\ntype modelsAPIResponse struct {\n\tData []modelEntry `json:\"data\"`\n}\n\n// fetchOpenAIModels GETs <baseURL>/models with Bearer auth and accepts both the\n// {data:[…]} envelope and a bare array shape used by various OpenAI-compatible servers.\nfunc fetchOpenAIModels(baseURL, apiKey string) ([]modelEntry, error) {\n\tif strings.TrimSpace(baseURL) == \"\" {\n\t\treturn nil, fmt.Errorf(\"api base is required\")\n\t}\n\tif strings.TrimSpace(apiKey) == \"\" {\n\t\treturn nil, fmt.Errorf(\"api key is required\")\n\t}\n\n\turl := strings.TrimRight(baseURL, \"/\") + \"/models\"\n\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"build request: %w\", err)\n\t}\n\treq.Header.Set(\"Authorization\", \"Bearer \"+apiKey)\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/model/online.go#L11-L47","documentation":"fetchOpenAIModels refuses to run without credentials: before any HTTP traffic it requires a non-blank API key because it sends that key as a Bearer Authorization header to the provider's /models endpoint. The guard is strings.TrimSpace(apiKey) == \"\", so a key of only spaces fails the same as an empty string.","triggerScenarios":"Invoking picoclaw's online model listing (fetchOpenAIModels at cmd/picoclaw/internal/model/online.go:29) with apiKey unset, \"\", or \"   \". The function fails immediately, before the GET to <baseURL>/models is built.","commonSituations":"Missing api key in the provider section of picoclaw's config, the key exported under a different env var name, the active profile not carrying the key, or a placeholder like <YOUR_KEY> that trims to nothing after template substitution.","solutions":["Set the API key for the provider in picoclaw config (or its env var) and retry the model-listing command.","Confirm the key value has no surrounding whitespace or quotes and is not still a placeholder.","Verify which config file and profile picoclaw loaded — an empty key usually means the wrong profile or file was picked up.","If the server genuinely needs no auth, note the check is unconditional: supply any non-empty token or use a different code path."],"exampleFix":"// before\nfetchOpenAIModels(\"https://api.example.com/v1\", \"\")\n// after\nfetchOpenAIModels(\"https://api.example.com/v1\", os.Getenv(\"EXAMPLE_API_KEY\"))","handlingStrategy":"validation","validationCode":"apiKey = strings.TrimSpace(apiKey)\nif apiKey == \"\" {\n    return fmt.Errorf(\"api key is required\")\n}\nentries, err := fetchOpenAIModels(baseURL, apiKey)","typeGuard":"func hasAPIKey(key string) bool {\n    return strings.TrimSpace(key) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Fail fast on empty credentials at the config-loading layer so commands never start with a blank key.","Log which config file and profile supplied the key when model listing runs.","Add a config lint check that reports providers with an empty api key."],"tags":["config","api-key","validation","go","openai-compatible"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}