pulumi/pulumi · error

could not import stack, failed to marshal stack state: %w

Error message

could not import stack, failed to marshal stack state: %w

What it means

ImportStack marshals the provided apitype.UntypedDeployment to JSON before writing it to the temp file. This error is returned if json.Marshal fails, which for UntypedDeployment practically means the struct contains values that cannot be serialized.

Source

Thrown at sdk/go/auto/local_workspace.go:827

			fmt.Errorf("failed to export stack, unable to unmarshall stack state: %w", err), stdout, stderr, errCode,
		)
	}

	return state, nil
}

// ImportStack imports the specified deployment state into a pre-existing stack.
// This can be combined with ExportStack to edit a stack's state (such as recovery from failed deployments).
func (l *LocalWorkspace) ImportStack(ctx context.Context, stackName string, state apitype.UntypedDeployment) error {
	f, err := os.CreateTemp(os.TempDir(), "")
	if err != nil {
		return fmt.Errorf("could not import stack. failed to allocate temp file: %w", err)
	}
	defer func() { contract.IgnoreError(os.Remove(f.Name())) }()

	bytes, err := json.Marshal(state)
	if err != nil {
		return fmt.Errorf("could not import stack, failed to marshal stack state: %w", err)
	}

	_, err = f.Write(bytes)
	if err != nil {
		return fmt.Errorf("could not import stack. failed to write out stack intermediate: %w", err)
	}

	stdout, stderr, errCode, err := l.runPulumiCmdSync(ctx, "stack", "import", "--file", f.Name(), "--stack", stackName)
	if err != nil {
		return newAutoError(fmt.Errorf("could not import stack: %w", err), stdout, stderr, errCode)
	}

	return nil
}

// StackOutputs gets the current set of Stack outputs from the last Stack.Up().
func (l *LocalWorkspace) StackOutputs(ctx context.Context, stackName string) (OutputMap, error) {
	// standard outputs

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Obtain state via ExportStack instead of hand-building apitype.UntypedDeployment
  2. Validate the Deployment JSON yourself with json.Valid / a test Marshal before calling ImportStack
  3. Check for invalid UTF-8 or control characters if the state was produced by another tool
  4. Log the failing field by marshaling components separately to isolate the bad value

Example fix

// before
err := ws.ImportStack(ctx, "dev", handEditedState)
// after - validate first
if !json.Valid(handEditedState.Deployment) {
  return errors.New("deployment bytes are not valid JSON")
}
err := ws.ImportStack(ctx, "dev", handEditedState)
Defensive patterns

Strategy: validation

Validate before calling

raw, err := json.Marshal(state)
if err != nil { return fmt.Errorf("state not marshalable: %w", err) }
if !json.Valid(state.Deployment) { return errors.New("deployment payload is not valid JSON") }

Type guard

func validDeployment(state apitype.UntypedDeployment) bool {
  return json.Valid(state.Deployment) && state.Version != 0
}

Try / catch

if err := ws.ImportStack(ctx, stackName, state); err != nil {
  if strings.Contains(err.Error(), "failed to marshal stack state") {
    return fmt.Errorf("state produced outside ExportStack is corrupt: %w", err)
  }
  return err
}

Prevention

When it happens

Trigger: Calling ImportStack with a state value that was hand-constructed or round-tripped through something producing non-marshalable content (e.g. invalid UTF-8 in deployment bytes) rather than coming from ExportStack.

Common situations: Custom tooling editing exported state and corrupting it; loading state from a source that produces invalid JSON data; unmarshal-then-marshal round trips losing valid types.

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/d06a510b535b75d3. Report an issue: GitHub.