nexu-io/open-design · error · Error

unsupported local_file refresh tool: ${toolName}

Error message

unsupported local_file refresh tool: ${toolName}

What it means

Thrown by executeLocalDaemonRefreshSource when source.type is 'local_file' but source.toolName is set to anything other than 'project_files.read_json' (the only currently-supported local_file tool; toolName defaults to project_files.read_json when omitted). Any other toolName under local_file is rejected before dispatch because no handler exists.

Source

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

  if (!response.ok) {
    throw new Error(`public_github_repository_metric request failed with ${response.status}`);
  }
  const parsed = await response.json() as Record<string, unknown>;
  const output: BoundedJsonObject = { toolName: 'public_github_repository_metric' };
  for (const field of selectGithubFields(input)) {
    const value = parsed[field];
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) {
      output[field] = value;
    }
  }
  return asBoundedRefreshOutput(output);
}

export async function executeLocalDaemonRefreshSource(options: ExecuteLocalDaemonRefreshSourceOptions): Promise<BoundedJsonObject> {
  if (options.source.type === 'local_file') {
    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>'}`);
  }

View on GitHub (pinned to 5be4028344)

Solutions

  1. For non-read_json tools, set source.type to 'daemon_tool' (the general case).
  2. If you want JSON-from-file behavior, either omit toolName or set it explicitly to 'project_files.read_json'.
  3. Re-check the supported matrix: local_file -> project_files.read_json only; daemon_tool -> project_files.search, project_files.read_json, git.summary, public_github_repository_metric.

Example fix

// before
source: { type: 'local_file', toolName: 'git.summary', input: {...} }
// after
source: { type: 'daemon_tool', toolName: 'git.summary', input: {...} }
Defensive patterns

Strategy: validation

Validate before calling

function coerceLocalFileSource(s: { type: string; toolName?: string }) {
  if (s.type === 'local_file' && s.toolName && s.toolName !== 'project_files.read_json') {
    return { ...s, type: 'daemon_tool' };
  }
  return s;
}

Type guard

function isLocalFileReadJson(s: unknown): boolean {
  return typeof s === 'object' && s !== null
    && (s as any).type === 'local_file'
    && ((s as any).toolName ?? 'project_files.read_json') === 'project_files.read_json';
}

Prevention

When it happens

Trigger: source = { type: 'local_file', toolName: 'git.summary' }; source = { type: 'local_file', toolName: 'project_files.search' }; source = { type: 'local_file', toolName: 'public_github_repository_metric' }.

Common situations: Model assumes any daemon tool can run against a local file source; config copy-pasted from a daemon_tool entry but type left as local_file; misunderstanding that local_file is a narrow alias only for read_json.

Related errors


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