oxidecomputer / oxidecomputer/omicron

Internet gateway accepts only one IP address, and the error blames the wrong object

Open
#11,044 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
572
Forks
97
Avg merge
2d 12h
Merged PRs (30d)
96

Description

While enabling networking tests for our PA suite, Claude found the following issue:

An internet gateway can hold at most one IP address. Attaching a second one fails — even
with a completely distinct name and IP — because of a UNIQUE index on
internet_gateway_ip_address (internet_gateway_id).

The failure surfaces as 400 ObjectAlreadyExists: already exists: internet-gateway-ip-address "<new-name>", naming an object that has never existed. The real
constraint is on the parent gateway, not the name, so the message points at the wrong thing.

Environment

  • Rack: racklette berlin
  • Software version: 22.0.0-0.ci+git35ee33351a2
  • Reproduced: 2026-08-10, via oxide CLI

Steps to reproduce

1. Attach the first address — succeeds
$ oxide internet-gateway address create --name addr-one --description first \
    --address 192.0.2.10 --gateway igw1 --vpc default --project igw-bug-repro
{
  "address": "192.0.2.10",
  "description": "first",
  "id": "19b7ee69-ebea-4550-90df-2a4b902d244a",
  "internet_gateway_id": "6c4c339a-152a-44df-8e24-4003dee26ac8",
  "name": "addr-one",
  "time_created": "2026-08-10T18:26:52.705847Z",
  "time_modified": "2026-08-10T18:26:52.705847Z"
}
2. Attach a second address, distinct name and distinct IP — fails
$ oxide internet-gateway address create --name addr-two --description second \
    --address 192.0.2.11 --gateway igw1 --vpc default --project igw-bug-repro
error
Error Response: status: 400 Bad Request; value: Error {
  error_code: Some("ObjectAlreadyExists"),
  message: "already exists: internet-gateway-ip-address \"addr-two\"",
  request_id: "19bd3017-73dd-45ce-9af3-07f2d009e06e"
}

addr-two did not exist before this call.

3. Confirm only one address is present
$ oxide internet-gateway address list --gateway igw1 --vpc default --project igw-bug-repro
[
  {
    "address": "192.0.2.10",
    "name": "addr-one",
    ...
  }
]
4. Delete the first address, then re-run the call that just failed — now succeeds

The name was never the problem, since we can now create it.

$ oxide internet-gateway address delete --address addr-one --gateway igw1 \
    --vpc default --project igw-bug-repro

$ oxide internet-gateway address create --name addr-two --description second \
    --address 192.0.2.11 --gateway igw1 --vpc default --project igw-bug-repro
{
  "address": "192.0.2.11",
  "name": "addr-two",
  "id": "62c417c1-e8c1-49cf-afba-1f989bec9e94",
  "internet_gateway_id": "6c4c339a-152a-44df-8e24-4003dee26ac8",
  ...
}
5. Contrast: two IP pool links on the same gateway both succeed
$ oxide internet-gateway ip-pool attach --name pool-link-one --ip-pool default \
    --gateway igw1 --vpc default --project igw-bug-repro
  "name": "pool-link-one",

$ oxide internet-gateway ip-pool attach --name pool-link-two --ip-pool default \
    --gateway igw1 --vpc default --project igw-bug-repro
  "name": "pool-link-two",

$ oxide internet-gateway ip-pool list --gateway igw1 --vpc default --project igw-bug-repro
    "name": "pool-link-one",
    "name": "pool-link-two",

Expected behavior

Either:

  • a gateway accepts multiple IP addresses, mirroring how it accepts multiple IP pools; or
  • if one address per gateway is deliberate, the API returns an error that says so
    (e.g. 400 explaining the gateway already has an address), and the limit is documented.

Actual behavior

The second attach fails with ObjectAlreadyExists naming an object that does not exist,
giving no indication that the gateway's capacity is the issue.

The misleading message comes from nexus/db-queries/src/db/datastore/vpc.rs:1926
(internet_gateway_attach_ip_address), which maps every database error through:

AsyncInsertError::DatabaseError(e) => public_error_from_diesel(
    e,
    ErrorHandler::Conflict(
        ResourceType::InternetGatewayIpAddress,
        name.as_str(),          // <-- the NEW row's name
    ),
),

The unique violation fires on internet_gateway_id, but the handler labels the conflict with
the name of the row being inserted — hence already exists: internet-gateway-ip-address "addr-two".

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in nexus/db-queries/src/db/datastore/vpc.rs:1926, in internet_gateway_attach_ip_address, and inspect how the unique violation on internet_gateway_id is mapped through AsyncInsertError and public_error_from_diesel. Check the surrounding networking behavior and tests, then make the reported result accurately reflect whether the gateway limit is intentional; done means the second-address case no longer blames the new address and the intended behavior is covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
62/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.