firebase / firebase/firebase-tools
Improve error message when invalid Firestore document path is specified in Firestore trigger definitions
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.3k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 84
Description
Wasn't really sure what to label this, so feel free to change it to something clearer.
### Related issues
Related, closed issue from 2018 fixed by fix of bug in grpc: firebase/firebase-functions#536 .
### [REQUIRED] Version info
Versions:
**node:** 16.13.0
**firebase-functions:** 3.18.1
**firebase-tools:** 10.2.2 (Also happened with 10.1.2)
**firebase-admin:** 10.0.2
### [REQUIRED] Test case
`index.ts` (looks mostly the same transpiled).
```
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
const app = admin.apps[0] ?? admin.initializeApp();
const testFunction = functions.https.onRequest(async (req, res) => {
try {
await app.firestore().collection('test').doc('test').set({ test: true });
res.send(true);
} catch (e) {
console.error(e);
res.send(false);
}
});
export default testFunction;
```
### [REQUIRED] Steps to reproduce
1. Compile the typescript
2. Run the function on the emulator using `firebase emulators:start`.
3. Send a request to `http://localhost:5001/{projectId}/{location}/default`
4. See in the console that we get an error with the note "Exception occurred in retry method that was not classified as transient". Complete error message below.
### [REQUIRED] Expected behavior
The collection "test" should have a document "test" with a value "test":true in it.
### [REQUIRED] Actual behavior
Sporadically, it seems to work, this might have something to do with cold starts? I don't know how that works on the emulator.
The logged exception:
```
Error: 2 UNKNOWN:
> at Object.callErrorFromStatus (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@grpc\grpc-js\build\src\call.js:31:26)
> at Object.onReceiveStatus (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@grpc\grpc-js\build\src\client.js:180:52)
> at Object.onReceiveStatus (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@grpc\grpc-js\build\src\client-interceptors.js:365:141)
> at Object.onReceiveStatus (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@grpc\grpc-js\build\src\client-interceptors.js:328:181)
> at C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@grpc\grpc-js\build\src\call-stream.js:182:78
> at processTicksAndRejections (node:internal/process/task_queues:78:11)
> Caused by: Error
> at WriteBatch.commit (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@google-cloud\firestore\build\src\write-batch.js:417:23)
> at DocumentReference.set (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\@google-cloud\firestore\build\src\reference.js:355:14)
> at C:\Users\Even\dev\flatz\app-2.0\functions\lib\index.js:9:62
> at C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:574:16
> at runFunction (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:547:15)
> at runHTTPS (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:573:11)
> at handler (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:493:23)
> at Layer.handle [as handle_request] (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\node_modules\express\lib\router\layer.js:95:5)
> at next (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\node_modules\express\lib\router\route.js:137:13)
> at Route.dispatch (C:\Users\Even\dev\flatz\app-2.0\functions\node_modules\firebase-tools\node_modules\express\lib\router\route.js:112:3) {
> code: 2,
> details: '',
> metadata: Metadata {
> internalRepr: Map(1) { 'content-type' => [Array] },
> options: {}
> },
> note: 'Exception occurred in retry method that was not classified as transient'
```
The error message showing up in firestore-debug.log:
```
Mar 10, 2022 7:58:49 PM com.google.cloud.datastore.emulator.impl.util.WrappedStreamObserver onError
INFO: operation failed: Invalid pattern. Reason: [76:] Expected '}' at end of capture expression.
java.lang.IllegalArgumentException: Invalid pattern. Reason: [76:] Expected '}' at end of capture expression.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:147)
at com.google.firebase.rules.eventflow.client.path.PathPattern.compileInternal(PathPattern.java:78)
at com.google.firebase.rules.eventflow.client.path.PathPattern.compile(PathPattern.java:50)
at com.google.firebase.rules.eventflow.client.EventRuleEvaluatorImpl.lambda$new$0(EventRuleEvaluatorImpl.java:58)
at com.google.common.collect.ImmutableList.forEach(ImmutableList.java:447)
at com.google.firebase.rules.eventflow.client.EventRuleEvaluatorImpl.(EventRuleEvaluatorImpl.java:52)
at com.google.firebase.rules.eventflow.client.EventRuleEvaluatorImpl.(EventRuleEvaluatorImpl.java:39)
at com.google.cloud.datastore.emulator.impl.events.EventManager.reportEvents(EventManager.java:84)
at com.google.cloud.datastore.emulator.impl.CloudFirestoreV1.commitHelper(CloudFirestoreV1.java:891)
at com.google.cloud.datastore.emulator.impl.CloudFirestoreV1.internalCommit(CloudFirestoreV1.java:780)
at com.google.cloud.datastore.emulator.impl.CloudFirestoreV1.commit(CloudFirestoreV1.java:416)
at com.google.cloud.datastore.emulator.impl.CloudFirestoreV1Router.commit(CloudFirestoreV1Router.java:137)
at com.google.cloud.datastore.emulator.firestore.v1.FirestoreV1GrpcAdapter$1.lambda$commit$8(FirestoreV1GrpcAdapter.java:156)
at com.google.cloud.datastore.emulator.firestore.v1.FirestoreV1GrpcAdapter.unary(FirestoreV1GrpcAdapter.java:67)
at com.google.cloud.datastore.emulator.firestore.v1.FirestoreV1GrpcAdapter.access$000(FirestoreV1GrpcAdapter.java:39)
at com.google.cloud.datastore.emulator.firestore.v1.FirestoreV1GrpcAdapter$1.commit(FirestoreV1GrpcAdapter.java:156)
at com.google.firestore.v1.FirestoreGrpc$MethodHandlers.invoke(FirestoreGrpc.java:1224)
at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182)
at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
at io.grpc.Contexts$ContextualizedServerCallListener.onHalfClose(Contexts.java:86)
at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35)
at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23)
at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40)
at io.grpc.Contexts$ContextualizedServerCallListener.onHalfClose(Contexts.java:86)
at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:331)
at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:866)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
```
I can see that it expects an ending bracket `}`, but I can't figure out where it expects it.
### Were you able to successfully deploy your functions?
Don't know if this is relevant, when I can't run them locally.
I've also attached my complete logs, but I'm pretty sure I've pasted the relevant parts.
[firebase-debug.log](https://github.com/firebase/firebase-functions/files/8226327/firebase-debug.log)
[firestore-debug.log](https://github.com/firebase/firebase-functions/files/8226328/firestore-debug.log)
Contributor guide
Research direction
No repository file or test is named. Start with the firebase emulators:start path and compare the Firestore emulator's PathPattern.compile stack entries with firestore-debug.log; reproduce the invalid trigger-path case, since the body currently shows only an HTTPS function. Done means the invalid path produces a clear actionable error instead of the raw gRPC failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- cli, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100