{"record":{"id":"e642e27fe98571e3","repo":"hashicorp/terraform","slug":"missing-field-in-set-s-s-e642e2","errorCode":null,"errorMessage":"missing field in set: %s.%s","messagePattern":"missing field in set: (.+?)\\.(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/legacy/helper/schema/field_reader_map.go","lineNumber":154,"sourceCode":"\t\t\treturn true\n\t\t}\n\t\tif strings.HasPrefix(k, prefix+\"#\") {\n\t\t\t// Ignore the count field\n\t\t\treturn true\n\t\t}\n\n\t\t// Split the key, since it might be a sub-object like \"idx.field\"\n\t\tparts := strings.Split(k[len(prefix):], \".\")\n\t\tidx := parts[0]\n\n\t\tvar raw FieldReadResult\n\t\traw, err = r.ReadField(append(address, idx))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tif !raw.Exists {\n\t\t\t// This shouldn't happen because we just verified it does exist\n\t\t\tpanic(\"missing field in set: \" + k + \".\" + idx)\n\t\t}\n\n\t\tset.Add(raw.Value)\n\n\t\t// Due to the way multimap readers work, if we've seen the number\n\t\t// of fields we expect, then exit so that we don't read later values.\n\t\t// For example: the \"set\" map might have \"ports.#\", \"ports.0\", and\n\t\t// \"ports.1\", but the \"state\" map might have those plus \"ports.2\".\n\t\t// We don't want \"ports.2\"\n\t\tcountActual[idx] = struct{}{}\n\t\tif len(countActual) >= countExpected {\n\t\t\treturn false\n\t\t}\n\n\t\treturn true\n\t})\n\tif !completed && err != nil {\n\t\treturn FieldReadResult{}, err","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/legacy/helper/schema/field_reader_map.go#L136-L172","documentation":"Panics inside MapFieldReader.readSet (internal/legacy/helper/schema/field_reader_map.go:154) while reconstructing a TypeSet from a flatmap. After a set-element key (e.g. \"ports.0\") is observed in the underlying Map, the reader re-reads that exact sub-address via ReadField and asserts Exists==true; the comment admits 'this shouldn't happen because we just verified it does exist'. When the re-read returns false the internal invariant is broken and the process panics.","triggerScenarios":"A flatmap or MultiMapReader state where a set element key is present (matches the set prefix and is not the count field) but its nested fields cannot be resolved by addrToSchema during the recursive ReadField. Common with a MultiMapReader layered over a dirty/partial state, or any custom MapReader whose Range and Access disagree.","commonSituations":"Corrupted or hand-edited state files, custom providers shimming legacy state, state upgrades where set element schemas changed between versions, and provider unit tests feeding hand-crafted flatmaps.","solutions":["Read the panic message: it prints the offending 'k.idx'; inspect that flatmap key in the state to see why its nested fields are unreadable.","If you supply a custom MapReader, make Access(k) and Range agree exactly (a key visited by Range must be retrievable by Access).","For real state corruption, restore from a known-good backup or run `terraform refresh`/`terraform apply -refresh-only` against a clean state.","Reproduce in a unit test with TestResourceData to isolate which schema/Elem shape causes the re-read miss, then fix the schema."],"exampleFix":"// before: custom MapReader whose Access/Range disagree\ntype Bad struct{ m map[string]string }\nfunc (b Bad) Access(k string) (string,bool){ return \"\", false } // never resolves\nfunc (b Bad) Range(f func(string,string) bool) bool { for k,v := range b.m { if !f(k,v){return false} }; return true }\n// after: Access and Range are consistent\nfunc (b Bad) Access(k string) (string,bool){ v,ok := b.m[k]; return v,ok }","handlingStrategy":"validation","validationCode":"// before calling Setok on a set reconstructed from a flatmap, ensure each\n// set element key is resolvable by the same MapReader that produced it.\nfunc flatmapSetConsistent(m schema.MapReader, prefix string) error {\n    bad := []string{}\n    m.Range(func(k, _ string) bool {\n        if strings.HasPrefix(k, prefix) && !strings.HasSuffix(k, \".#\") {\n            if _, ok := m.Access(k); !ok { bad = append(bad, k) }\n        }\n        return true\n    })\n    if len(bad) > 0 { return fmt.Errorf(\"unresolvable set keys: %v\", bad) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// last-resort: recover from the internal panic at a provider boundary\ndefer func() {\n    if r := recover(); r != nil {\n        log.Printf(\"[ERROR] schema readSet panic: %v\", r)\n    }\n}()","preventionTips":["Keep custom MapReader.Access and Range strictly consistent.","Never hand-edit flatmap state for sets; use the SDK readers/writers.","Add round-trip unit tests (write then read) for every set-typed schema."],"tags":["terraform","schema","set","flatmap","state","panic","internal-invariant"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}