Skip to content

[Bug] unable to import reflect-metadata within workflow bundle #1858

@hmvien

Description

@hmvien

What are you really trying to do?

Our Temporal TypeScript worker bundles class-based workflows that use Inversify’s decorators (and therefore reflect-metadata). These workflows are instantiated within the workflow entry point using container.get(WorkflowIdentifier).

We implement workflows as classes to enhance workflow and activity modularity and to make it easier to write unit tests.

Describe the bug

When reuseV8Context remains enabled (the default), the workflow is unable to start up with error

TypeError: Cannot define property decorate, object is not extensible
    at Function.defineProperty (<anonymous>)
    at reflect-metadata/Reflect.js:35:0
    at reflect-metadata/Reflect.js:146:0
    at reflect-metadata/Reflect.js:29:0
    at reflect-metadata/Reflect.js:55:5

This used to work in temporal-sdk 1.11.8, but breaks as soon as we upgrade to a later sdk version.

Temporal freezes the global Reflect object inside the reusable workflow isolate, so reflect-metadata cannot add its helpers.

Setting reuseV8Context to false fixes the issue, but does increase our memory and cpu footprint.

Minimal Reproduction

workflows.ts

import "reflect-metadata";

import { Container, injectable } from "inversify";

@injectable()
class Workflows {
  async execute(): Promise<void> {
    // This workflow doesn't need to do anything; the error occurs as soon as
    // reflect-metadata tries to patch the frozen Reflect object.
  }
}

const container = new Container();
container.bind(Workflows).toSelf();

export default async function () {
  const workflow = container.get(Workflows);
  return await workflow.execute();
}

worker.ts

import path from "node:path";
import { fileURLToPath } from "node:url";

import { NativeConnection, Worker } from "@temporalio/worker";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function main(): Promise<void> {
  const reuseV8Context = process.env.REUSE_V8_CONTEXT !== "false";

  console.info(
    `[repro] Starting worker with reuseV8Context=${reuseV8Context.toString()}`,
  );

  const connection = await NativeConnection.connect({
    address: "localhost:7233",
  });
  const worker = await Worker.create({
    connection,
    namespace: "dev",
    taskQueue: "dev",
    reuseV8Context,
    workflowsPath: path.join(__dirname, "workflows.ts"),
  });

  await worker.run();
}

main().catch((error) => {
  console.error("[repro] Worker creation failed with error:");
  console.error(error);
  process.exitCode = 1;
});

Execute with

1. tsx worker.ts
2. temporal workflow start --task-queue dev --type TestWorkflow

Environment/Versions

  • OS and processor: macOS
  • Temporal Version: 1.13
  • Are you using Docker or Kubernetes or building Temporal from source? No

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions