firebase / firebase/firebase-tools

Dataconnect migration for UUID fields with `@default(expr: "uuidV4()")` runs invalid SQL which causes the deploy to hang. (operator does not exist: uuid = text)

Open
#11,003 1 comment 0 reactions 0 assignees View on GitHub
api: dataconnect reproducible type: bug
Dominant language
TypeScript
Stars
4.5k
Forks
1.3k
Avg merge
1d 12h
Merged PRs (30d)
84

Description

### [REQUIRED] Environment info

**firebase-tools:** 15.28.2

**Platform:** macOS

dataconnect-emulator-3.4.18

PostgreSQL 18.4

### [REQUIRED] Test case

Data Connect schema:

```gql
type User @table {
id: UUID! @default(expr: "uuidV4()") # deploy, then replace `UUID` with `String`
name: String
}
```

Reproducible with no queries or mutations, no foreign key or second table, and no records in the database. The uuid field also doesn't have to be the table's key (even though it is above).

### [REQUIRED] Steps to reproduce

#### For the deploy:

1. initialize dataconnect - `firebase init dataconnect`
2. create a schema, including at least one `UUID` field with a default as above
3. deploy the schema - `firebase deploy --only dataconnect`
4. replace `UUID` with `String`
5. deploy the updated schema - `firebase deploy --only dataconnect`

#### For the emulator:

1. initialize dataconnect - `firebase init dataconnect`
2. create a schema, including at least one `UUID` field with a default as above
3. start the emulator - `firebase emulators:start --only dataconnect`
4. edit the schema while the emulator is running, to replace `UUID` with `String`

### [REQUIRED] Expected behavior

- The deploy should succeed. I believe `UUID -> String` is a supported Postgres conversion.
- If the migration is unsupported, the deploy should throw an error describing where it is incompatible.
- The deterministic SQL error should be surfaced for the user instead of being retried

### [REQUIRED] Actual behavior

The migration fails when it runs this query: `SELECT uuid_generate_v4() = replace((uuid_generate_v4())::text, '-'::text, ''::text)` because the left side is uuid and the right is text, which has no implicit cast.

The emulator and the deploy run into the same error. The emulator surfaces it straight away, but the deploy hangs and retries in the background until it times out.

#### Deploy

Hangs on:
```
dataconnect: Generating SQL schema migrations to be compatible...
```

Error:
```
Error: Task index 0 failed: timed out after 300000ms.
```

The backend operation continues retrying after the CLI times out, which can block other operations until it times out a few minutes later: `Error: Request to https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main?allowMissing=true&validateOnly=false had HTTP Error: 409, unable to queue the operation`

Debug logs:
```
[2026-08-29T11:47:31.296Z] >>> [apiv2][query] PATCH https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main allowMissing=true&validateOnly=false
[2026-08-29T11:47:31.296Z] >>> [apiv2][body] PATCH https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","datasources":[{"postgresql":{"database":"dataconnect-uuid-bug-database","cloudSql":{"instance":"projects/dataconnect-uuid-bug/locations/europe-west2/instances/dataconnect-uuid-bug-instance"},"schemaValidation":"COMPATIBLE"}}],"source":{"files":[{"path":"schema.gql","content":"type User @table {\n id: String! @default(expr: \"uuidV4()\")\n name: String\n}\n"}]}}
[2026-08-29T11:47:31.614Z] <<< [apiv2][status] PATCH https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main 200
[2026-08-29T11:47:31.614Z] <<< [apiv2][body] PATCH https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04","metadata":{"@type":"type.googleapis.com/google.firebase.dataconnect.v1.OperationMetadata","createTime":"2026-08-29T11:47:31.611867303Z","target":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","verb":"update","requestedCancellation":false,"apiVersion":"v1"},"done":false}
[2026-08-29T11:47:31.615Z] >>> [apiv2][query] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 [none]
[2026-08-29T11:47:31.652Z] <<< [apiv2][status] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 200
[2026-08-29T11:47:31.652Z] <<< [apiv2][body] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04","metadata":{"@type":"type.googleapis.com/google.firebase.dataconnect.v1.OperationMetadata","createTime":"2026-08-29T11:47:31.611867303Z","target":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","verb":"update","requestedCancellation":false,"apiVersion":"v1"},"done":false}
[2026-08-29T11:47:32.154Z] [LRO Poller] Retrying task index 0
[2026-08-29T11:47:32.155Z] >>> [apiv2][query] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 [none]
[2026-08-29T11:47:32.198Z] <<< [apiv2][status] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 200
[2026-08-29T11:47:32.198Z] <<< [apiv2][body] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04","metadata":{"@type":"type.googleapis.com/google.firebase.dataconnect.v1.OperationMetadata","createTime":"2026-08-29T11:47:31.611867303Z","target":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","verb":"update","requestedCancellation":false,"apiVersion":"v1"},"done":false}
[2026-08-29T11:47:33.199Z] [LRO Poller] Retrying task index 0

(this repeats for a few mins)

[2026-08-29T11:50:35.839Z] >>> [apiv2][query] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 [none]
[2026-08-29T11:50:36.011Z] <<< [apiv2][status] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 200
[2026-08-29T11:50:36.011Z] <<< [apiv2][body] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04","metadata":{"@type":"type.googleapis.com/google.firebase.dataconnect.v1.OperationMetadata","createTime":"2026-08-29T11:47:31.611867303Z","target":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","verb":"update","requestedCancellation":false,"apiVersion":"v1"},"done":false}
[2026-08-29T11:51:36.012Z] [LRO Poller] Retrying task index 0
[2026-08-29T11:51:36.013Z] >>> [apiv2][query] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 [none]
[2026-08-29T11:51:36.119Z] <<< [apiv2][status] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 200
[2026-08-29T11:51:36.119Z] <<< [apiv2][body] GET https://firebasedataconnect.googleapis.com/v1/projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04 {"name":"projects/dataconnect-uuid-bug/locations/europe-west2/operations/operation-1788004051353-65a2e22567fcd-fb2e21d1-55ee0a04","metadata":{"@type":"type.googleapis.com/google.firebase.dataconnect.v1.OperationMetadata","createTime":"2026-08-29T11:47:31.611867303Z","target":"projects/dataconnect-uuid-bug/locations/europe-west2/services/dataconnect-uuid-bug-service/schemas/main","verb":"update","requestedCancellation":false,"apiVersion":"v1"},"done":false}
Task index 0 failed: timed out after 300000ms.

Error: Task index 0 failed: timed out after 300000ms.
```

#### Emulator

Error:
```
SQL Connect Emulator: SQL_MIGRATION - SQL migration error: failed to plan diffs: failed to generate diff: pq: operator does not exist: uuid = text
```

Debug logs:
```
[2026-08-29T11:19:34.958Z] I0829 12:19:34.950973 5694 load.go:31] Reloading schema and connectors...
{"metadata":{"emulator":{"name":"dataconnect"},"message":"I0829 12:19:34.950973 5694 load.go:31] Reloading schema and connectors...\n"}}
2026-08-29 11:19:34.985 GMT [0] ERROR: operator does not exist: uuid = text at character 27
2026-08-29 11:19:34.985 GMT [0] HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
2026-08-29 11:19:34.985 GMT [0] STATEMENT: SELECT uuid_generate_v4() = replace((uuid_generate_v4())::text, '-'::text, ''::text)
[2026-08-29T11:19:34.986Z] E0829 12:19:34.985993 5694 service.go:140] [projects/emulator/locations/emulator/services/dataconnect-bug dec7] failed to migrate schema: SQL migration error: failed to plan diffs: failed to generate diff: pq: operator does not exist: uuid = text
{"metadata":{"emulator":{"name":"dataconnect"},"message":"E0829 12:19:34.985993 5694 service.go:140] [projects/emulator/locations/emulator/services/dataconnect-bug dec7] failed to migrate schema: SQL migration error: failed to plan diffs: failed to generate diff: pq: operator does not exist: uuid = text\n"}}
[2026-08-29T11:19:34.987Z] I0829 12:19:34.987199 5694 control.go:100] [projects/emulator/locations/emulator/services/dataconnect-bug 3806] UpdateResources(): done
Main schema: sources: schema/schema.gql [82B]
Connector "example": sources: example/mutations.gql [0B] example/queries.gql [0B]
{"metadata":{"emulator":{"name":"dataconnect"},"message":"I0829 12:19:34.987199 5694 control.go:100] [projects/emulator/locations/emulator/services/dataconnect-bug 3806] UpdateResources(): done\nMain schema: sources: schema/schema.gql [82B] \nConnector \"example\": sources: example/mutations.gql [0B] example/queries.gql [0B] \n"}}
[2026-08-29T11:19:34.989Z] I0829 12:19:34.989665 5694 collector.go:107] schema extensions wrote into "/Users/jack/x/x/dataconnect-bug/dataconnect/.dataconnect/schema"
Generated sources: main/mutation.gql [3515B] main/input.gql [4165B] main/relation.gql [1189B] main/query.gql [1358B] prelude.gql [84995B]
I0829 12:19:34.989674 5694 load.go:109] Finished reloading
{"metadata":{"emulator":{"name":"dataconnect"},"message":"I0829 12:19:34.989665 5694 collector.go:107] schema extensions wrote into \"/Users/jack/x/x/dataconnect-bug/dataconnect/.dataconnect/schema\"\nGenerated sources: main/mutation.gql [3515B] main/input.gql [4165B] main/relation.gql [1189B] main/query.gql [1358B] prelude.gql [84995B] \nI0829 12:19:34.989674 5694 load.go:109] Finished reloading\n"}}
```

Contributor guide

Open the contributing guide

Research direction

Start with the schema.gql reproduction and run the emulator migration, then trace the migration diff planning that produces `SELECT uuid_generate_v4() = replace(...)`. Compare how the emulator and deploy handle this deterministic SQL error. Done means the UUID-to-String migration either succeeds or reports a clear, non-retried error in both paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
postgresql, typescript
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.