OpenHands/OpenHands · error · Error

Reading plugin files is only available on a local backend.

Error message

Reading plugin files is only available on a local backend.

What it means

Thrown by PluginsService.getPluginFileContent() when the active backend is cloud. This method reads a single plugin file's content from the agent-server's local disk via FileClient.downloadFile(). Plugin files (source code, skills, config) only exist on the local agent-server's filesystem, so reading them is meaningless and would fail against a cloud backend. The guard surfaces this as a clear error rather than a confusing 404/network error from the cloud endpoint.

Source

Thrown at src/api/plugins-service.ts:132

    } catch {
      return [];
    }
  }

  /**
   * Fetch one plugin file's content for the detail-modal viewer. `basePath` is
   * the plugin directory reported by the agent-server (`path`/`install_path`)
   * and `relativePath` a POSIX path from the plugin's `files` listing.
   *
   * Local backend only — plugin files live on the local agent-server's disk.
   * Errors propagate so the caller can render a load-error state.
   */
  static async getPluginFileContent(
    basePath: string,
    relativePath: string,
  ): Promise<PluginFileContent> {
    if (getActiveBackend().backend.kind === "cloud") {
      throw new Error(
        "Reading plugin files is only available on a local backend.",
      );
    }

    const buffer = await new FileClient(
      getAgentServerClientOptions(),
    ).downloadFile(`${basePath}/${relativePath}`);
    if (isLikelyBinary(buffer)) {
      return { kind: "binary", text: null };
    }
    return {
      kind: "text",
      text: new TextDecoder("utf-8", { fatal: false }).decode(buffer),
    };
  }
}

export default PluginsService;

View on GitHub (pinned to 500b4c533e)

Solutions

  1. Switch to a local backend to view plugin file contents.
  2. Gate the plugin file viewer / detail modal on backend.kind === 'local' and show a placeholder message for cloud.
  3. If viewing plugin source is needed in cloud, link to the plugin's upstream git repository instead of reading local files.

Example fix

// Gate the file viewer on local backend
const { backend } = getActiveBackend();
if (backend.kind !== 'local') {
  return <Placeholder>File viewing requires a local backend.</Placeholder>;
}
<FileViewer path={path} />
Defensive patterns

Strategy: validation

Validate before calling

// Guard before calling getPluginFileContent
import { getActiveBackend } from '#/api/backend-registry/active-store';

if (getActiveBackend().backend.kind !== 'local') {
  // Show a placeholder instead of fetching file content
  return { kind: 'text', text: 'File viewing requires a local backend.' };
}

Type guard

function isLocalActiveBackend(): boolean {
  return getActiveBackend().backend.kind === 'local';
}

Try / catch

try {
  const content = await PluginsService.getPluginFileContent(basePath, relativePath);
} catch (error) {
  if (error instanceof Error && error.message.includes('only available on a local backend')) {
    setFileViewMode('unsupported');
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: Any call to PluginsService.getPluginFileContent(basePath, relativePath) when getActiveBackend().backend.kind === 'cloud'. Typically triggered when the plugin detail modal tries to render the file viewer while the user is on a cloud backend.

Common situations: The user opens a plugin's detail modal (which lists files and shows content) while connected to a Cloud backend, or the plugin file viewer component loads without checking the active backend kind.

Related errors


AI-assisted analysis of OpenHands/OpenHands@500b4c533e (2026-08-12). Data as JSON: /api/errors/a31f92c53a67c3f4. Report an issue: GitHub.