---
title: Output value errors
url: /docs/iac/operations/troubleshooting/output-values/
---


Pulumi resource properties are represented as [Outputs](/docs/iac/concepts/inputs-outputs/#outputs) rather than plain values, because their real values are not known until the resource providing them has actually been created or updated. This is central to how Pulumi builds a dependency graph and runs `pulumi preview`, but it also means a whole family of ordinary-looking code fails in ways that are not obvious from the error message alone: pulling a value out of an Output too early, branching on it directly, or trying to read it the way you would read a variable in a normal script.

This page collects the errors and symptoms this pattern produces, matched to their cause and fix. If you have not already read [Accessing single outputs with Apply](/docs/iac/concepts/inputs-outputs/apply/) and [Working with multiple outputs](/docs/iac/concepts/inputs-outputs/all/), start there for the underlying model; this page assumes that background and focuses on diagnosis.

## Calling toString on an output is not supported

If you concatenate, log, or otherwise convert an output value to a string directly instead of going through `apply` or an equivalent helper, Pulumi does not raise an error by default. Instead it silently substitutes a placeholder message in place of the value:

<!-- chooser: language -->

<!-- option: typescript -->
```typescript
// Silently produces a broken string; no exception is thrown.
console.log(`The bucket name is ${bucket.bucketName}`);

```

The resulting string contains literal text such as <code>Calling [toString] on an [Output<T>] is not supported</code>, followed by guidance to use `apply` or `pulumi.interpolate` instead. The same placeholder mechanism backs JSON serialization: calling `JSON.stringify()` on an output, or letting one flow into a JSON body, produces a <code>Calling [toJSON] on an [Output<T>] is not supported</code> string rather than the value.

<!-- /option -->

<!-- option: python -->
```python
# Produces a broken string and logs a warning; no exception is raised.
print(f"The bucket name is {bucket.bucket}")

```

The resulting string contains literal text such as `Calling __str__ on an Output[T] is not supported`, and Pulumi also logs a warning. Passing an output to `json.dumps()` fails outright with `TypeError: Object of type Output is not JSON serializable`; use `Output.json_dumps()` instead.

<!-- /option -->

<!-- option: go -->
Go’s static typing generally prevents this: passing a `pulumi.StringOutput` where a `string` is expected is a compile error, not a runtime surprise. The fix is the same as in other languages: resolve the value inside `ApplyT` and pass the result on, rather than trying to read a `pulumi.StringOutput` as a plain `string`.

<!-- /option -->

<!-- option: csharp -->
```csharp
// Silently produces a broken string; no exception is thrown.
Console.WriteLine($"The bucket name is {bucket.BucketName}");

```

The resulting string contains literal text such as <code>Calling [ToString] on an [Output<T>] is not supported</code>, followed by guidance to use `Apply` or `Output.Format` instead.

<!-- /option -->

<!-- /chooser -->

Because this fails by producing a string rather than throwing, the mistake is easy to miss during development: the program runs, `pulumi up` succeeds, and the placeholder text only surfaces later, embedded in a log line, a generated config file, or a downstream resource property. To catch it immediately instead, set the environment variable `PULUMI_ERROR_OUTPUT_STRING=true` (TypeScript, Python, and C#) while developing; with it set, the same code throws an exception at the point of the mistake instead of returning the placeholder text.

The fix is the same across languages: keep the output inside an `apply`/`Apply` callback (or Python's equivalent), or use the string-composition helpers built for this purpose — `pulumi.interpolate` and `pulumi.concat` in TypeScript, `Output.concat`/`Output.format`/`Output.json_dumps` in Python, `pulumi.Sprintf`/`pulumi.JSONMarshal` in Go, and `Output.Format` in C#. See [Accessing single outputs with apply](/docs/iac/concepts/inputs-outputs/apply/) and [Working with multiple outputs](/docs/iac/concepts/inputs-outputs/all/) for examples.

## A value that "worked" outside `apply` stops working further down the program

A subtler version of the same mistake: the value read out of an output inside an `apply`/`Apply` callback is only available inside that callback (or in another output derived from it). Assigning it to an outer variable and using that variable later, outside the callback, does not do what it looks like it does:

```typescript
let bucketName: string | undefined;
bucket.bucketName.apply((name) => {
    bucketName = name; // Runs asynchronously, later.
});

// bucketName is still undefined here — the apply callback above has not run yet.
new aws.s3.BucketObject("readme", {
    bucket: bucketName, // Still undefined here.
    key: "README.md",
});
```

The `apply` callback runs asynchronously once the output resolves, which in general is after the rest of your program's top-level code has already finished running. Reading the outer variable before that happens gets you `undefined`. This is not a timing bug you can fix by adding a delay: the callback runs when the engine resolves the dependency graph, not on your program's clock.

The fix is to never let the plain value leave the callback chain. Either do the downstream work inside the same `apply`, chain a further `apply` on the result, or — usually the better option — pass the output itself as the input to whatever needs it, and let Pulumi resolve the dependency:

```typescript
new aws.s3.BucketObject("readme", {
    bucket: bucket.bucketName, // Pass the Output directly; no apply needed here at all.
    key: "README.md",
});
```

Before reaching for `apply` at all, check whether the resource you are configuring can accept the output directly as one of its inputs, since most resource inputs accept outputs as well as plain values. (A handful of positions cannot, such as a resource's logical name; see [Using an output as a resource name, a map key, or a loop bound](#using-an-output-as-a-resource-name-a-map-key-or-a-loop-bound) below.)

## Branching program logic on an output produces the wrong branch, or an error at synthesis time

Code like `if (someOutput) { ... } else { ... }` does not do what it looks like. In a dynamically typed language the output object itself is what gets evaluated, not its eventual value, so an `if` on an output is generally always true (an object is a true value in a boolean context) regardless of what the output resolves to. In a statically typed language this is usually a compile error instead, because an `Output<bool>` is not a `bool`.

This only applies to values that come from resources — outputs, in other words. A value that is already known when your program starts, such as a stack configuration setting read with `pulumi.Config`, an environment variable, or a hardcoded flag, is a plain value and branches on it exactly the way you would expect; there is nothing to fix there.

When the branch genuinely depends on a resource's output, the branch itself has to move inside `apply`, which means both possible outcomes need to be expressed as values rather than as program structure:

```typescript
// Does not work: the branch is evaluated on the Output object, not its value.
if (vpc.vpcId) {
    // ...
}

// Works: derive a new Output whose value depends on the branch.
const subnetCount = vpc.vpcId.apply((id) => (id.startsWith("vpc-0") ? 3 : 6));
```

Because the two branches can no longer be expressed as separate blocks of resource-creation code (only as separate values), a runtime branch on an output usually means restructuring the program so both possible sets of resources are described unconditionally, and the output only decides between values passed into them, or so the branch is on a plain config value instead of a resource output.

## Using an output as a resource name, a map key, or a loop bound

A resource's logical name (the first argument to its constructor) and the loop bounds or map keys you use while authoring a program all need to be known plain values while your program is still being evaluated, before any resource actually exists. An output is a promise of a future value, so passing one in any of these positions fails to compile in TypeScript, Go, and C#, where these positions are typed as plain strings or numbers. Python's dynamic typing does not catch this at compile time, but the SDK still checks it in most of these positions at runtime: passing an output as a resource's logical name raises `TypeError: Expected resource name to be a string`, and using one as a loop bound (`range(some_output)`) raises `TypeError: 'Output' object cannot be interpreted as an integer`. Using an output as a dictionary key is the one position that does not raise — Python falls back to comparing and hashing the output object itself, so the lookup silently keys on the wrong thing rather than on the value the output will eventually resolve to.

If you need resource names or counts to depend on data that is only known once a resource has been created, the two usual fixes are: derive that data from configuration or another data source that is already available as a plain value at program-authoring time, or, if it genuinely cannot be known until an earlier resource has been provisioned, restructure the dependent resources so they are provisioned in a later, separate stack that consumes the first stack's outputs through a [`StackReference`](/docs/iac/concepts/stacks/#stackreferences).

## Trying to read an output's value synchronously

Trying to `await` an output or read its value synchronously either doesn't compile, fails at runtime, or quietly returns placeholder text instead of the value — Pulumi has no supported way to block your program and pull a resolved value out of an output synchronously. This is deliberate: doing so would defeat the whole point of building a dependency graph up front, since the engine would need the value before it has finished figuring out in what order to create anything.

If what you actually want is to read a value that a Pulumi program has already produced, from outside that program, the output is not the right place to reach for it. Use one of:

- `pulumi stack output <name>` from the CLI, to read a single stack output after a deployment.
- A [`StackReference`](/docs/iac/concepts/stacks/#stackreferences) from another Pulumi program, to consume one stack's outputs as another stack's inputs.
- The [Automation API](/docs/iac/concepts/automation-api/)'s `outputs` field on the result of running an update (for example `res.Outputs` in Go, or the equivalent in your language's Automation API package), when driving deployments programmatically.

All three read a value that has already resolved, rather than trying to unwrap an in-flight output from inside the same program that created it.

