{"id":"451edf2afd065d96","repo":"docker/cli","slug":"expected-a-pointer-to-a-struct-got-nil-pointer","errorCode":null,"errorMessage":"expected a pointer to a struct, got nil pointer","messagePattern":"expected a pointer to a struct, got nil pointer","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/formatter/reflect.go","lineNumber":31,"sourceCode":"\n// MarshalJSON marshals x into json\n// It differs a bit from encoding/json MarshalJSON function for formatter\nfunc MarshalJSON(x any) ([]byte, error) {\n\tm, err := marshalMap(x)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.Marshal(m)\n}\n\n// marshalMap marshals x to map[string]any\nfunc marshalMap(x any) (map[string]any, error) {\n\tval := reflect.ValueOf(x)\n\tif val.Kind() != reflect.Pointer {\n\t\treturn nil, fmt.Errorf(\"expected a pointer to a struct, got %v\", val.Kind())\n\t}\n\tif val.IsNil() {\n\t\treturn nil, errors.New(\"expected a pointer to a struct, got nil pointer\")\n\t}\n\tvalElem := val.Elem()\n\tif valElem.Kind() != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"expected a pointer to a struct, got a pointer to %v\", valElem.Kind())\n\t}\n\ttyp := val.Type()\n\tm := make(map[string]any)\n\tfor i := 0; i < val.NumMethod(); i++ {\n\t\tk, v, err := marshalForMethod(typ.Method(i), val.Method(i))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif k != \"\" {\n\t\t\tm[k] = v\n\t\t}\n\t}\n\treturn m, nil\n}","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/docker/cli/blob/e9452d6e785f6e365712b9d71bd7517591773c86/cli/command/formatter/reflect.go#L13-L49","documentation":"formatter.marshalMap (used by formatter.MarshalJSON for `--format json` output) requires a non-nil pointer to a struct because it reflects over the value's methods (Name(), ID(), etc.) to build the JSON map. It first checks the kind is reflect.Pointer, then checks val.IsNil(); a typed nil pointer passes the kind check but cannot have its methods safely invoked, so it is rejected explicitly instead of panicking inside reflection.","triggerScenarios":"Calling formatter.MarshalJSON(x) where x is a typed nil pointer, e.g. `var c *containerContext; formatter.MarshalJSON(c)` — typically from a formatter Context whose per-item context struct was never allocated before being handed to the JSON writer.","commonSituations":"Developing a custom formatter/context type in the docker CLI codebase and passing an unallocated pointer into the write path; refactors that change a formatter constructor to return nil on error while callers still marshal the result; unit tests feeding nil fixtures into formatter code.","solutions":["Allocate the context struct before marshalling: pass &myContext{...} rather than a declared-but-nil pointer.","Trace the call site producing the nil — a constructor returning (nil, err) whose error was ignored is the usual culprit; handle the error instead.","If nil is a legitimate 'no data' state, guard the MarshalJSON call and emit an empty object or skip the row."],"exampleFix":"// before\nvar imgCtx *imageContext\nb, err := formatter.MarshalJSON(imgCtx)\n// after\nimgCtx := &imageContext{i: img, trunc: trunc}\nb, err := formatter.MarshalJSON(imgCtx)","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["go","reflection","formatter","nil-pointer","json"],"analyzedSha":"e9452d6e785f6e365712b9d71bd7517591773c86","analyzedAt":"2026-08-01T07:09:22.474Z","schemaVersion":2}