nexu-io/open-design · error · Error

unsupported local daemon refresh tool: ${options.source.tool

Error message

unsupported local daemon refresh tool: ${options.source.toolName}

What it means

Thrown by executeLocalDaemonRefreshSource when source.type is 'daemon_tool' but source.toolName is not in the recognized set (validated by isLocalDaemonRefreshToolName). The placeholder '<missing>' is interpolated when toolName is undefined/null, signalling that the field was omitted entirely rather than mis-spelled.

Source

Thrown at apps/daemon/src/live-artifacts/refresh.ts:726

    const toolName = options.source.toolName ?? 'project_files.read_json';
    if (toolName !== 'project_files.read_json') {
      throw new Error(`unsupported local_file refresh tool: ${toolName}`);
    }
    return executeProjectFilesReadJson({
      ...options,
      source: {
        ...options.source,
        type: 'daemon_tool',
        toolName,
      },
    });
  }

  if (options.source.type !== 'daemon_tool') {
    throw new Error('local daemon refresh sources require source.type daemon_tool or local_file');
  }
  if (!isLocalDaemonRefreshToolName(options.source.toolName)) {
    throw new Error(`unsupported local daemon refresh tool: ${options.source.toolName ?? '<missing>'}`);
  }

  switch (options.source.toolName) {
    case 'project_files.search':
      return executeProjectFilesSearch(options);
    case 'project_files.read_json':
      return executeProjectFilesReadJson(options);
    case 'git.summary':
      return executeGitSummary(options);
    case 'public_github_repository_metric':
      return executePublicGithubRepositoryMetric(options);
  }
}

View on GitHub (pinned to 5be4028344)

Solutions

  1. Use one of the registered tool names exactly: 'project_files.search', 'project_files.read_json', 'git.summary', 'public_github_repository_metric'.
  2. If toolName is missing, add it explicitly to the source object.
  3. When adding a new tool, register it in isLocalDaemonRefreshToolName AND add a case to the switch in executeLocalDaemonRefreshSource; both must change together.

Example fix

// before
source: { type: 'daemon_tool', toolName: 'github_metric', input: {...} }
// after
source: { type: 'daemon_tool', toolName: 'public_github_repository_metric', input: {...} }
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN_TOOLS = new Set(['project_files.search','project_files.read_json','git.summary','public_github_repository_metric']);
function assertRefreshTool(t: unknown): asserts t is string {
  if (typeof t !== 'string' || !KNOWN_TOOLS.has(t)) throw new Error(`unknown refresh tool: ${String(t)}`);
}

Type guard

function isKnownRefreshTool(v: unknown): v is string {
  return typeof v === 'string' && new Set(['project_files.search','project_files.read_json','git.summary','public_github_repository_metric']).has(v);
}

Prevention

When it happens

Trigger: source = { type: 'daemon_tool' } (no toolName); source = { type: 'daemon_tool', toolName: 'github.metric' }; source = { type: 'daemon_tool', toolName: 'files.read' }; toolName uses a retired or future name not yet registered.

Common situations: Model invents a plausible tool name not in the supported list; copy-paste left toolName out; refactor renamed a tool but old payloads still reference it; tool name typo or case mismatch (the registry is case-sensitive).

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/7a55d18de0fa090d. Report an issue: GitHub.