Skip to content

fix(functions): surface unhandled exceptions during functions deployment - #11257

Merged
ajperel merged 1 commit into
mainfrom
ajp/functions-deploy-errors
Oct 12, 2026
Merged

ajperel merged 1 commit into
mainfrom
ajp/functions-deploy-errors

Conversation

@ajperel

@ajperel ajperel commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

Prevent Fabricator.applyPlan from silently ignoring rejected applyUpserts or applyDeletes promises at logger.debug, which could allow a failed Cloud Functions deployment to proceed to function deletions and exit with code 0. Also move logOpStart inside wrapOperation and guard the RUNTIMES display-name lookup so per-endpoint logging errors are isolated to the affected function.

Part of #6989

Scenarios Tested

  • Manually tested with firebase deploy --only functions.
  • Unit tests verifying applyPlan throws a FirebaseError and skips subsequent phases when applyUpserts or applyDeletes rejects unexpectedly.
  • Unit tests verifying logOpStart handles unrecognized runtime strings and isolates errors inside wrapOperation.

Sample Commands

firebase deploy --only functions

@ajperel
ajperel added this pull request to stack #11259 October 10, 2026 00:21

@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 improves error handling during Cloud Functions deployment by ensuring that unhandled exceptions in both upsert and delete operations are captured and re-thrown as FirebaseErrors, preventing silent failures. It also includes comprehensive unit tests to verify this behavior and adds a small fix to runtime label resolution. The review feedback correctly identified that the promise rejection reason casting is unsafe and that the exit code used in the new error handling should be consistent with the repository's standard (exit: 1).

Comment thread src/deploy/functions/release/fabricator.ts Outdated
upsertErrors.length === 1 ? { original: upsertErrors[0] } : { children: upsertErrors };
throw new FirebaseError("Fabricator.applyUpserts encountered an unhandled exception", {
...opts,
exit: 2,

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

Using exit: 2 is inconsistent with other errors in this file and the rest of the codebase, which standardly use exit: 1 for general deployment failures.

To maintain consistency and adhere to the standard exit codes, consider changing this to exit: 1 or omitting it entirely to let it default to 1.

Suggested change
exit: 2,
exit: 1,
References
  1. Throw FirebaseError for expected, user-facing errors. If the error is due to a violation of a precondition, specify a non-zero exit code ({ exit: 1 }). (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I chatted with AI a whole lot about this and it was convinced we should do 2 since it's an unknown internal error and not a user caused error. I defer to @joehan

Comment thread src/deploy/functions/release/fabricator.ts Outdated
deleteErrors.length === 1 ? { original: deleteErrors[0] } : { children: deleteErrors };
throw new FirebaseError("Fabricator.applyDeletes encountered an unhandled exception", {
...opts,
exit: 2,

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

Using exit: 2 is inconsistent with other errors in this file and the rest of the codebase, which standardly use exit: 1 for general deployment failures.

To maintain consistency and adhere to the standard exit codes, consider changing this to exit: 1 or omitting it entirely to let it default to 1.

Suggested change
exit: 2,
exit: 1,
References
  1. Throw FirebaseError for expected, user-facing errors. If the error is due to a violation of a precondition, specify a non-zero exit code ({ exit: 1 }). (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I chatted with AI a whole lot about this and it was convinced we should do 2 since it's an unknown internal error and not a user caused error. I defer to @joehan

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.

I buy your logic here. We've not been super consistent about error code handling accross the codebase, but thats no reason to not try to be better here.

@ajperel
ajperel force-pushed the ajp/functions-deploy-errors branch from f31cfbb to 3917c4c Compare October 10, 2026 00:38
@ajperel
ajperel requested review from joehan and wandamora and removed request for joehan and wandamora October 10, 2026 00:48
@ajperel
ajperel force-pushed the ajp/functions-deploy-errors branch from 3917c4c to 97aaf11 Compare October 12, 2026 16:10
@ajperel
ajperel requested review from joehan and wandamora October 12, 2026 16:13
Comment thread src/deploy/functions/release/fabricator.ts

@wandamora wandamora 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.

LGTM % nit and Joe's feedback on the exit code.

@ajperel
ajperel force-pushed the ajp/functions-deploy-errors branch 2 times, most recently from 59ab205 to fbd7dad Compare October 12, 2026 17:33
### Description
Prevent `Fabricator.applyPlan` from silently ignoring rejected `applyUpserts` or `applyDeletes` promises at `logger.debug`, which could allow a failed Cloud Functions deployment to proceed to function deletions and exit with code 0. Also move `logOpStart` inside `wrapOperation` and guard the `RUNTIMES` display-name lookup so per-endpoint logging errors are isolated to the affected function.

Part of #6989

### Scenarios Tested
- Manually tested with `firebase deploy --only functions`.
- Unit tests verifying `applyPlan` throws a `FirebaseError` and skips subsequent phases when `applyUpserts` or `applyDeletes` rejects unexpectedly.
- Unit tests verifying `logOpStart` handles unrecognized runtime strings and isolates errors inside `wrapOperation`.

### Sample Commands
```bash
firebase deploy --only functions
```
@ajperel
ajperel force-pushed the ajp/functions-deploy-errors branch from fbd7dad to 97a2869 Compare October 12, 2026 21:41
@ajperel
ajperel merged commit 0a06f01 into main Oct 12, 2026
51 checks passed
@ajperel
ajperel deleted the ajp/functions-deploy-errors branch October 12, 2026 22:03
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.

4 participants