sipeed/picoclaw · error

command unavailable: config not loaded

Error message

command unavailable: config not loaded

What it means

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.

Source

Thrown at pkg/agent/agent_command.go:183

				toolCount, isConnected := connected[serverName]
				servers = append(servers, commands.MCPServerInfo{
					Name:      serverName,
					Enabled:   serverCfg.Enabled,
					Deferred:  serverIsDeferred(cfg.Tools.MCP.Discovery.Enabled, serverCfg),
					Connected: isConnected,
					ToolCount: toolCount,
				})
			}

			sort.Slice(servers, func(i, j int) bool {
				return strings.ToLower(servers[i].Name) < strings.ToLower(servers[j].Name)
			})

			return servers
		},
		ListMCPTools: func(ctx context.Context, serverName string) ([]commands.MCPToolInfo, error) {
			if cfg == nil {
				return nil, fmt.Errorf("command unavailable: config not loaded")
			}

			serverName = strings.TrimSpace(serverName)
			if serverName == "" {
				return nil, fmt.Errorf("server name is required")
			}

			resolvedName := ""
			var serverCfg config.MCPServerConfig
			for name, candidate := range cfg.Tools.MCP.Servers {
				if strings.EqualFold(name, serverName) {
					resolvedName = name
					serverCfg = candidate
					break
				}
			}
			if resolvedName == "" {
				return nil, fmt.Errorf("MCP server '%s' is not configured", serverName)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Ensure configuration is loaded and passed before the agent loop starts accepting commands
  2. If embedding, call the same config-loading path the CLI uses (internal.LoadConfig) and pass the result into the agent loop constructor
  3. Retry the command after startup completes; treat this as a transient init-order signal in long-running processes
Defensive patterns

Strategy: type-guard

Validate before calling

// Before exposing commands, assert the runtime has config:
if rt == nil || cfg == nil { /* defer command registration */ }

Type guard

func hasLoadedConfig(cfg *config.Config) bool { return cfg != nil }

Try / catch

tools, err := rt.ListMCPTools(ctx, name)
if err != nil && strings.Contains(err.Error(), "config not loaded") {
    // init-order issue: load config and retry once
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/76af773c67ce720d. Report an issue: GitHub.