Docs / Troubleshooting

When nxip says no

Some refusals are not bugs: nxip is doing what it was designed to do, and the message alone does not say why. Each section below is one error, as you see it, with why it happens and what to do.

Errors on this page

No matching IPV4 IP pool found

What you see. From the API, the CLI or Terraform:

No matching IPV4 IP pool found for environment 'preprod' in region 'ukwest'. Please create a pool first.

From Terraform provider 0.7.1, the error’s title is No matching pool for `preprod` / `ukwest` / `IPV4`, with the message above underneath. For IPv6 it says IPV6.

Why. A subnet finds its pool by environment, region and family. That is the design: a team asks for address space without knowing, or needing to know, which pool it comes from. So the pool has to exist before the subnet asks.

In Terraform, the usual cause is a pool and a subnet in the same configuration, with the subnet givingenvironment, region and family as plain text. Terraform only orders resources by their references, so it sees no link between the two, creates both at the same time, and the subnet asks for a pool that does not exist yet. The pool is created fine, and a second terraform apply usually succeeds, which makes it look like a flaky error. It is not.

Fix: the pool is in the same configuration

Take the values from the pool’s own attributes. The reference is what tells Terraform to create the pool first.

resource "nxip_pool" "preprod_uk_west" {
  name        = "preprod-uk-west"
  cidr        = "10.50.0.0/16"
  family      = "IPV4"
  environment = "preprod"
  region      = "ukwest"
}

resource "nxip_subnet" "legacy_app" {
  environment   = nxip_pool.preprod_uk_west.environment
  region        = nxip_pool.preprod_uk_west.region
  family        = nxip_pool.preprod_uk_west.family
  prefix_length = 24
  name          = "legacy-app"
}

Or keep the plain text and add depends_on. It works, but Terraform’s own docs prefer references, because they also keep the values from drifting apart.

resource "nxip_subnet" "legacy_app" {
  environment   = "preprod"
  region        = "ukwest"
  family        = "IPV4"
  prefix_length = 24
  name          = "legacy-app"

  depends_on = [nxip_pool.preprod_uk_west]
}

Fix: the pool is in another configuration

This is the intended team workflow: one team owns the pools, the others ask for subnets. Plain text is right here. Create the pool first (apply the configuration that owns it), or bring an existing one under Terraform with terraform import, then apply the subnet.

If neither applies

Check the values for a typo: ukwest and uk-west are different regions to nxip, and the match is exact. And check you are in the organization you think you are: a pool in one organization is invisible from every other.

Terraform plans to replace a pool

What you see. In the plan:

  # nxip_pool.preprod_uk_west must be replaced
-/+ resource "nxip_pool" "preprod_uk_west" {
      ~ name        = "preprod-uk-west" -> "preprod-uk-west-2" # forces replacement

And sometimes, during the apply:

Cannot delete IP Pool 'preprod-uk-west'. It currently has 2 active subnet(s). Please release the subnets first.

Why. A pool’s name, environment, region andfamily cannot be changed in place, so changing any of them makes Terraform destroy the pool and create a new one. Its cidr is different: it is resized in place, as long as every subnet in the pool still fits. That is not a replacement.

What happens next depends on the pool’s subnets:

  • Subnets that take the changed attribute from the pool (for exampleenvironment = nxip_pool.preprod_uk_west.environment) are replaced with it. Terraform destroys them first, so the apply succeeds. But every one of them is created again from scratch: it gets a new ID, an auto-allocated one gets whichever block is free at that moment, which is not always the block it had, and every address registered in it is deleted with it.
  • Otherwise the subnets stay where they are, the pool cannot be deleted while it holds them, and the apply stops with the error above. Nothing has been changed at that point.

Fix. Before applying, look for forces replacement on any nxip_pool in the plan. If the apply has already stopped, undo the change and the plan is clean again. If you really do mean to replace the pool, its subnets have to go first, and they will not come back with the same addresses unless theircidr is pinned.

terraform plan | grep -B3 "forces replacement"

By design? Yes. A pool’s environment, region and family are how subnets find it, so changing one is a different pool, not an edit. Refusing to delete a pool that still holds subnets is deliberate too: deleting a whole address plan as a side effect of one changed attribute is never what anyone wants.

Organization not found

What you see. From the API:

Organization not found.

From the Terraform provider, which names the attribute to check:

the `organization` attribute ("ACME Ltd") is not this API key's own organization and is not one of its customers, or the link between them has ended

Why. You asked to act for a customer organization, and one of the conditions for that is not met. The value has to be the organization’s ID (not its name), for a customer of your key’s own organization, with the link between you still in place. Every other case gets this identical answer, on purpose: if a wrong ID and someone else’s real ID gave different answers, anyone could use the difference to find out which organizations exist.

Fix. Check, in this order:

  1. The value is the organization’s ID, not its name. ACME Ltd is a name, and is never accepted.
  2. The ID is right. It is on the Customers page in the dashboard, and in GET /v1/organizations/children.
  3. The API key belongs to you, the provider, not to the customer. A customer’s own key never needs the organization set.
  4. The customer has not unlinked you. Either side can end a link, and it applies from the very next request.

Where the organization is set:

# Terraform: the provider's organization attribute, or the environment
provider "nxip" {
  organization = "<customer organization id>"
}
export NXIP_ORGANIZATION=<customer organization id>

# CLI
npx nxip-cli plan --organization <customer organization id>

# API
-H "x-nxip-organization: <customer organization id>"

In the dashboard, the customer switcher takes you back to your own organization when this happens. More on acting for customers in Customer organizations.

This endpoint cannot be used on behalf of another organization

What you see. A 400:

This endpoint cannot be used on behalf of another organization.

Why. An organization was named (the x-nxip-organization header, which the provider’sorganization, NXIP_ORGANIZATION and the CLI’s --organization all set), on a request that an organization only ever makes for itself, whoever is acting. That covers API keys, removing members, and managing customers and links (including a customer’s view of its own provider). It is by design: those decide who can reach an organization, which is the customer’s to control, never its provider’s. The request is refused outright, rather than quietly run against your own organization, so a script never changes the wrong place.

Fix. Make that request without the organization set. The usual cause isNXIP_ORGANIZATION left exported in a shell from earlier work on a customer:

unset NXIP_ORGANIZATION

In the dashboard, switch back to your own organization first.

More than one landing point matches

What you see. A 409 when creating a subnet by environment and region, for example:

More than one landing point matches environment 'production', region 'us-east-1', including payments-eks-prod-service-cidr (10.109.0.0/24) and us-east-1 region block (10.109.16.0/20). Pass parentSubnetId to say which one this belongs in.

Why. A subnet with a kind that sits directly in a pool (a region block, a VPC, a site) is, by default, a landing point: ordinary subnet requests for the same environment, region and family, with noparent_subnet_id, are placed inside it. That is what lets a team ask for a subnet without knowing how the address plan is laid out. With one landing point, the answer is obvious. With two or more, the request does not say which one it means, so nxip refuses rather than guess.

Before provider 0.9 there was no way to have it otherwise: every subnet with a kind directly in the pool was a landing point, and any number of them could share an environment and region. The commonest way to end up with two was the Kubernetes CIDR modules, whose pod and service ranges carry a kind so that they stay in the pool and never land inside a VPC. Next to a region block, that made two landing points for one key, and every ordinary request there got this error.

From provider 0.9: a kind without being a landing point

nxip_subnet has a landing_point argument. It defaults to true when the subnet has a kind and no parent_subnet_id, so nothing you already have changes. Set it tofalse and the subnet keeps its kind, stays directly in the pool, and is never a placement target:

resource "nxip_subnet" "payments_eks_prod_service_cidr" {
  environment   = "production"
  region        = "us-east-1"
  family        = "IPV4"
  prefix_length = 24
  kind          = "service-cidr"
  landing_point = false
}

The terraform-nxip-modules EKS, AKS and GKE modules setlanding_point = false on every range they create, so with provider 0.9 and the matching module release this error no longer occurs next to them. The region block is once again the only landing point for its environment and region, and your other subnets need no parent_subnet_id. Changing landing_point is an in-place update, never a replacement.

Fix: say where the subnet belongs

If the key really does have two landing points (two region blocks, or a region block and a VPC, both meant to receive requests), pass the parent explicitly. The error names the candidates. In Terraform, reference the one you mean:

resource "nxip_subnet" "payments_production" {
  parent_subnet_id = nxip_subnet.production_us_east_region.id
  family           = "IPV4"
  prefix_length    = 24
  name             = "Payments team"
}

Or: keep one landing point per environment and region

If you want requests by environment and region to keep working unchanged, give each environment, region and family exactly one landing point, and either nest any further structure inside it with parent_subnet_id or leave it in the pool with landing_point = false. A kind on a nested subnet is a label only, and never makes it a landing point.

Hit something that is not here? Tell us. Back to all docs, or the full API reference.