Skip to content

fix: make storage, apphosting, and dataconnect emulator startup resilient - #10934

Draft
christhompsongoogle wants to merge 4 commits into
mainfrom
startup_failed
Draft

fix: make storage, apphosting, and dataconnect emulator startup resilient#10934
christhompsongoogle wants to merge 4 commits into
mainfrom
startup_failed

Conversation

@christhompsongoogle

Copy link
Copy Markdown
Contributor

Description

Prevent missing configurations for Storage, App Hosting, and Data Connect emulators from crashing emulators:start and blocking the rest of the emulator suite:

  • Fall back to default open rules with a warning for Storage emulator when rules are unconfigured on non-demo projects.
  • Gracefully skip Data Connect emulator with a warning if no service configurations are present in firebase.json or startup fails.
  • Gracefully skip App Hosting emulator with a warning if package manager / start command auto-detection fails or startup fails.
  • Clean up failed instances in EmulatorRegistry when start() fails.

Fixes b/546204399

Scenarios Tested

  • Unit tests in storage/rules/config.spec.ts for missing storage config, missing rules, and demo/non-demo projects.
  • Unit tests in controller.spec.ts for Data Connect shouldStart handling.
  • Unit tests in registry.spec.ts for failed emulator startup cleanup.
  • App Hosting serve unit tests.

Sample Commands

  • firebase init emulators (select all emulators)
  • firebase emulators:start --project=ctfdc1

Description

Scenarios Tested

Sample Commands

…ient

### Description
Prevent missing configurations for Storage, App Hosting, and Data Connect emulators from crashing emulators:start and blocking the rest of the emulator suite:
- Fall back to default open rules with a warning for Storage emulator when rules are unconfigured on non-demo projects.
- Gracefully skip Data Connect emulator with a warning if no service configurations are present in firebase.json or startup fails.
- Gracefully skip App Hosting emulator with a warning if package manager / start command auto-detection fails or startup fails.
- Clean up failed instances in EmulatorRegistry when start() fails.

Fixes b/546204399

### Scenarios Tested
- Unit tests in storage/rules/config.spec.ts for missing storage config, missing rules, and demo/non-demo projects.
- Unit tests in controller.spec.ts for Data Connect shouldStart handling.
- Unit tests in registry.spec.ts for failed emulator startup cleanup.
- App Hosting serve unit tests.

### Sample Commands
- firebase init emulators (select all emulators)
- firebase emulators:start --project=ctfdc1
@wiz-9635d3485b

wiz-9635d3485b Bot commented Aug 13, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 8 Medium 3 Low
Software Management Finding Software Management Findings -
Total 8 Medium 3 Low

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request prevents blocking startup errors for the entire emulator suite when configurations for Storage, App Hosting, or Data Connect emulators are missing or fail to start. Instead of throwing fatal errors, the emulators now log warnings and gracefully skip startup. The review feedback highlights a potential resource leak in the emulator registry if a port check fails after starting, and suggests extracting duplicated logging logic in the storage rules configuration to improve maintainability.

Comment thread src/emulator/registry.ts Outdated
Comment on lines 41 to 44
} catch (err: unknown) {
this.clear(instance.getName());
throw err;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If instance.start() succeeds but the subsequent port check portUtils.waitForPortUsed fails (e.g., due to a timeout), the emulator process is left running in the background. Since this.clear(instance.getName()) is called in the catch block, the registry loses track of the instance, meaning a subsequent cleanShutdown() or stopAll() will not clean up the orphaned process.

To prevent resource leaks, attempt to stop the emulator instance in the catch block before clearing it from the registry.

    } catch (err: unknown) {
      try {
        await instance.stop();
      } catch (stopErr) {
        // Ignore stop errors to avoid masking the original startup error
      }
      this.clear(instance.getName());
      throw err;
    }

Comment on lines +35 to +42
} else {
storageLogger.logLabeled(
"WARN",
"storage",
"Did not find a Storage rules file specified in a firebase.json config file. The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration.",
);
}
throw new FirebaseError(
"Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration",
);
return defaultStorageRules();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for logging the default rules warning/bullet is duplicated between the case where storageConfig is missing (lines 28-41) and where storageConfig.rules is missing (lines 48-60).

To adhere to the repository style guide on reducing nesting and improving maintainability, consider extracting this logging logic into a helper function.

Example:

function logDefaultRulesWarning(projectId: string, storageLogger: EmulatorLogger): void {
  if (Constants.isDemoProject(projectId)) {
    storageLogger.logLabeled(
      "BULLET",
      "storage",
      `Detected demo project ID "${projectId}", using a default (open) rules configuration.`
    );
  } else {
    storageLogger.logLabeled(
      "WARN",
      "storage",
      "Did not find a Storage rules file specified in a firebase.json config file. The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration."
    );
  }
}
References
  1. Reduce nesting as much as possible and consider helper functions to encapsulate complex branching. (link)

@joehan

joehan commented Aug 14, 2026

Copy link
Copy Markdown
Member

/joe-review

EmulatorLogger.forEmulator(Emulators.DATACONNECT).logLabeled(
"ERROR",
"dataconnect",
`Failed to start Data Connect emulator: No valid Data Connect configuration detected in firebase.json`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [Error Remediation] Missing remediation instructions

Rationale: CLI error messages should provide explicit instructions on how to resolve the issue. The warning for missing Data Connect configuration should advise running firebase init dataconnect (similar to how Hosting emulator advises running firebase init hosting).

Suggested Fix:

    EmulatorLogger.forEmulator(Emulators.DATACONNECT).logLabeled(
      "ERROR",
      "dataconnect",
      `Failed to start Data Connect emulator: No valid Data Connect configuration detected in firebase.json. Run ${clc.bold("firebase init dataconnect")} to configure it.`,
    );

const options = createMockOptions("apphosting", {
apphosting: { startCommand: "npm run dev" },
});
expect(shouldStart(options, Emulators.APPHOSTING)).to.be.true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [Testing Discipline] Missing test for lockfile auto-detection

Rationale: The PR implements auto-detection for the App Hosting start command by looking for a lockfile in the backend root. However, there is no test verifying that the emulator starts when a lockfile is present.

Suggested Fix: Add a unit test stubbing fs.existsSync using sinon to return true for a lockfile path.

    it("should start apphosting emulator if start command is not set but lockfile is present", () => {
      const existsStub = sinon.stub(fs, "existsSync");
      existsStub.withArgs(sinon.match(/package-lock.json/)).returns(true);
      try {
        const options = createMockOptions("apphosting", {
          apphosting: { rootDirectory: "./my-app" },
        });
        expect(shouldStart(options, Emulators.APPHOSTING)).to.be.true;
      } finally {
        existsStub.restore();
      }
    });

return {
only,
config,
cwd: process.cwd(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: [TypeScript Precision] Legacy as any in test helper

Rationale: The helper createMockOptions uses as any on return and any for the configValues map. Typing this more precisely (e.g. Record<string, unknown>) would improve type safety in tests.

Suggested Fix:

function createMockOptions(
  only: string | undefined,
  configValues: Record<string, unknown>,
): Options {
  ...
  return {
    only,
    config,
    cwd: process.cwd(),
    project: "test-project",
  } as unknown as Options;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants