Skip to content

ProxyProviderNotResolvedException thrown even after resolveProxyProviders(...) #375

@Natashkinsasha

Description

@Natashkinsasha

ProxyProviderNotResolvedException thrown even after resolveProxyProviders(...) inside cls.run(...)

Summary

We occasionally get ProxyProviderNotResolvedException when accessing a proxy provider, despite resolving it right before usage inside clsService.run(...).

This happens rarely and non-deterministically in production workers (BullMQ). It looks like a race between proxy resolution and access, or a context boundary issue.

Error

ProxyProviderNotResolvedException: Cannot access the property "getHash" on the Proxy provider UserSettingDocument because is has not been resolved yet and has been registered with the "strict: true" option. Make sure to call "await cls.resolveProxyProviders()" before accessing the Proxy provider.
    at Function.create (/app/node_modules/nestjs-cls/dist/src/lib/proxy-provider/proxy-provider.exceptions.js:69:16)
    at checkAllowedPropertyAccess (/app/node_modules/nestjs-cls/dist/src/lib/proxy-provider/proxy-provider-manager.js:180:81)
    at Object.get (/app/node_modules/nestjs-cls/dist/src/lib/proxy-provider/proxy-provider-manager.js:119:51)
    at QuestPrizeGeneratorWorker.resolveProxyProviders (/apps/meta-server/src/@job/common/job-worker.ts:46:59)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at QuestPrizeGeneratorWorker.prepareContext (/apps/meta-server/src/@job/common/context-job.worker.ts:24:5)
    at QuestPrizeGeneratorWorker.execute (/apps/meta-server/src/@job/common/context-job.worker.ts:36:5)
    at /app/node_modules/bullmq/src/classes/worker.ts:910:26
    at Worker.retryIfFailed (/app/node_modules/bullmq/src/classes/worker.ts:1174:16)

What we do

@Injectable()
export class ExampleService {
  constructor(
    private readonly clsService: ClsService,
    @Inject(UserSettingDocument) private readonly settingDocument: UserSettingDocument, // proxied provider
  ) {}

  public example() {
    return this.clsService.run(async () => {
      await this.clsService.resolveProxyProviders([UserSettingDocument]);
      // Rarely throws ProxyProviderNotResolvedException here:
      this.settingDocument.getHash();
      return true;
    });
  }
}

Expected behavior

No exception is thrown after await clsService.resolveProxyProviders([...]) has completed within the same cls.run(...) scope.

Actual behavior

Very rarely (hard to reproduce locally), an exception is thrown saying the proxy has not been resolved yet. It happens in BullMQ worker jobs.

Environment

  • nestjs-cls: 5.0.1
  • NestJS: 10.3.5
  • Node.js: 18.20.0
  • Package manager: yarn 1.22.19
  • Platform: BullMQ workers (separate process), Linux (Docker)
  • BullMQ: 5.8.4
  • strict mode: strict: true for proxy provider registration
  • cls setup: ClsModule.forRoot({ global: true, middleware: { mount: true } })
  • Repository type: monorepo (private)

Minimal reproduction (suggested outline)

I haven’t been able to produce a stable repro, but a flaky one could be:

  • Start a BullMQ worker that wraps each job in clsService.run(...).
  • Register a proxy provider with strict: true.
  • Inside a job handler:
    1. await cls.resolveProxyProviders([ProxyToken])
    2. await new Promise(r => setImmediate(r)) (force a macro-task hop)
    3. Access a method on the proxied provider.
      Sometimes this still throws in our environment.

If there’s an official recommended pattern, I’m happy to test it and provide a full repro repo.

Workarounds tried

  • Moving resolveProxyProviders earlier in the call chain — still happens.
  • Ensuring access happens synchronously right after resolve (reduced frequency but didn’t eliminate).

Thanks a lot! Happy to provide more logs or build a repro if you can point me to the right pattern to ensure context consistency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    solvedThe question was answered or the problem was solved

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions