{"record":{"id":"76af773c67ce720d","repo":"sipeed/picoclaw","slug":"command-unavailable-config-not-loaded","errorCode":null,"errorMessage":"command unavailable: config not loaded","messagePattern":"command unavailable: config not loaded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/agent_command.go","lineNumber":183,"sourceCode":"\t\t\t\ttoolCount, isConnected := connected[serverName]\n\t\t\t\tservers = append(servers, commands.MCPServerInfo{\n\t\t\t\t\tName:      serverName,\n\t\t\t\t\tEnabled:   serverCfg.Enabled,\n\t\t\t\t\tDeferred:  serverIsDeferred(cfg.Tools.MCP.Discovery.Enabled, serverCfg),\n\t\t\t\t\tConnected: isConnected,\n\t\t\t\t\tToolCount: toolCount,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tsort.Slice(servers, func(i, j int) bool {\n\t\t\t\treturn strings.ToLower(servers[i].Name) < strings.ToLower(servers[j].Name)\n\t\t\t})\n\n\t\t\treturn servers\n\t\t},\n\t\tListMCPTools: func(ctx context.Context, serverName string) ([]commands.MCPToolInfo, error) {\n\t\t\tif cfg == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"command unavailable: config not loaded\")\n\t\t\t}\n\n\t\t\tserverName = strings.TrimSpace(serverName)\n\t\t\tif serverName == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"server name is required\")\n\t\t\t}\n\n\t\t\tresolvedName := \"\"\n\t\t\tvar serverCfg config.MCPServerConfig\n\t\t\tfor name, candidate := range cfg.Tools.MCP.Servers {\n\t\t\t\tif strings.EqualFold(name, serverName) {\n\t\t\t\t\tresolvedName = name\n\t\t\t\t\tserverCfg = candidate\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif resolvedName == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"MCP server '%s' is not configured\", serverName)","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/agent_command.go#L165-L201","documentation":"The ListMCPTools runtime hook (backing an agent slash-command that lists an MCP server's tools) found cfg == nil: the agent loop's configuration was never loaded into the command runtime. Every stateful runtime command built in this closure guards on cfg, and this one refuses to enumerate MCP tools from an unconfigured process.","triggerScenarios":"Invoking the /mcp tools-list style command before LoadConfig/config initialization completed, or on an AgentLoop constructed without wiring configuration (unit tests embedding the runtime standalone, headless embedding that skipped config load).","commonSituations":"Embedding picoclaw's agent runtime in a custom binary without loading config; race where a slash command arrives during startup before config load finishes; tests that instantiate the command table directly.","solutions":["Ensure configuration is loaded and passed before the agent loop starts accepting commands","If embedding, call the same config-loading path the CLI uses (internal.LoadConfig) and pass the result into the agent loop constructor","Retry the command after startup completes; treat this as a transient init-order signal in long-running processes"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Before exposing commands, assert the runtime has config:\nif rt == nil || cfg == nil { /* defer command registration */ }","typeGuard":"func hasLoadedConfig(cfg *config.Config) bool { return cfg != nil }","tryCatchPattern":"tools, err := rt.ListMCPTools(ctx, name)\nif err != nil && strings.Contains(err.Error(), \"config not loaded\") {\n    // init-order issue: load config and retry once\n}","preventionTips":["Register runtime commands only after config load completes","In embeddings, reuse the CLI's LoadConfig path before loop construction","Gate command surfaces on cfg != nil"],"tags":["runtime","configuration","mcp","initialization"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}