microsoft/aspire · error · InvalidOperationException

Embedded telemetry hook script

Error message

Embedded telemetry hook script '{resourceName}' was not found in the CLI assembly.

What it means

TelemetryHookInstaller.ReadEmbeddedText loads hook script text (shell/PowerShell) that is embedded as a manifest resource in the CLI assembly. This error means the resource name was not found — the assembly is missing an expected embedded script. It signals a broken or partially built/deployed CLI binary rather than a user input problem.

Solutions

  1. Rebuild/reinstall the CLI from official sources (or run ./build.sh / dotnet publish with default settings) so embedded resources are included.
  2. If you customized resource file names/namespaces, update the resourceName strings in TelemetryHookInstaller to match the actual embedded resource names.
  3. Disable aggressive trimming/resource stripping for the CLI publish, or verify the resource appears in the assembly's manifest.
  4. Run `aspire --version` to confirm you are on a complete, official build rather than a partial local artifact.

Example fix

// before
// csproj omits the hook scripts from embedded resources
// after
<ItemGroup>
  <EmbeddedResource Include="Agents/Hooks/Scripts/*.sh;Agents/Hooks/Scripts/*.ps1" />
</ItemGroup>
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    await installer.InstallAsync(...);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Embedded telemetry hook script"))
{
    // the CLI build is missing embedded resources; reinstall the CLI
}

Prevention

When it happens

Trigger: Thrown when Assembly.GetManifestResourceStream(resourceName) returns null for a shell or PowerShell telemetry hook script — i.e. the resource was not embedded at build time, or a trimmed/published assembly dropped it.

Common situations: Running a hand-built or locally modified CLI where the .sh/.ps1 embedded resources were not included; IL trimming or single-file publish stripping resources; copying a partially built binary; mismatch between resource naming convention and code after a rename.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/d99a31bc3b36bb52. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Agents/Hooks/TelemetryHookInstaller.cs:66

        var powerShellContent = ReadEmbeddedText(PowerShellResourceName);

        await WriteFileIfChangedAsync(shellPath, shellContent, cancellationToken);
        await WriteFileIfChangedAsync(powerShellPath, powerShellContent, cancellationToken);

        // Ensure the shell script is executable so a `bash <path>` (or direct exec) hook entry works.
        // Spawning `chmod` would add PATH/shell failure modes, so use the platform API directly.
        TrySetExecutable(shellPath);

        return new TelemetryHookScripts(shellPath, powerShellPath);
    }

    private static string NormalizeToLf(string content)
        => content.Replace("\r\n", "\n").Replace("\r", "\n");

    private static string ReadEmbeddedText(string resourceName)
    {
        using var stream = typeof(TelemetryHookInstaller).Assembly.GetManifestResourceStream(resourceName)
            ?? throw new InvalidOperationException($"Embedded telemetry hook script '{resourceName}' was not found in the CLI assembly.");
        using var reader = new StreamReader(stream, s_utf8NoBom);
        return reader.ReadToEnd();
    }

    private async Task WriteFileIfChangedAsync(string path, string content, CancellationToken cancellationToken)
    {
        // Skip the write when the content already matches so a running hook isn't disturbed and the
        // file mtime stays stable across repeated `agent init` runs.
        if (File.Exists(path))
        {
            try
            {
                var existing = await File.ReadAllTextAsync(path, s_utf8NoBom, cancellationToken);
                if (string.Equals(existing, content, StringComparison.Ordinal))
                {
                    return;
                }
            }

View on GitHub (pinned to 25830f84bd)