{"record":{"id":"86a997a968dcb8d0","repo":"d2lang/d2","slug":"unknown-object-of-type-t","errorCode":null,"errorMessage":"unknown object of type %T","messagePattern":"unknown object of type %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2renderers/d2svg/d2svg.go","lineNumber":2920,"sourceCode":"\tmarkdown := newMarkdownRenderer(diagram.FontFamily, diagram.MonoFontFamily, inlineTheme)\n\tfor _, obj := range allObjects {\n\t\tif c, is := obj.(d2target.Connection); is {\n\t\t\tlabelMask, err := drawConnection(buf, isolatedDiagramHash, c, markers, idToShape, sketch, inlineTheme, markdown)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif labelMask != \"\" {\n\t\t\t\tlabelMasks = append(labelMasks, labelMask)\n\t\t\t}\n\t\t} else if s, is := obj.(d2target.Shape); is {\n\t\t\tlabelMask, err := drawShape(buf, appendixItemBuf, diagramHash, s, sketch, inlineTheme, markdown)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t} else if labelMask != \"\" {\n\t\t\t\tlabelMasks = append(labelMasks, labelMask)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"unknown object of type %T\", obj)\n\t\t}\n\t}\n\t// add all appendix items afterwards so they are always on top\n\tfmt.Fprint(buf, appendixItemBuf)\n\n\tif diagram.Legend != nil && (len(diagram.Legend.Shapes) > 0 || len(diagram.Legend.Connections) > 0) {\n\t\tlegendBuf := &bytes.Buffer{}\n\t\terr := RenderLegend(legendBuf, diagram, diagramHash, inlineTheme)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfmt.Fprint(buf, legendBuf)\n\t}\n\n\t// Note: we always want this since we reference it on connections even if there end up being no masked labels\n\tleft, top, w, h := dimensions(diagram, pad, tl, br)\n\n\tif diagram.Legend != nil && (len(diagram.Legend.Shapes) > 0 || len(diagram.Legend.Connections) > 0) {","sourceCodeStart":2902,"sourceCodeEnd":2938,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2renderers/d2svg/d2svg.go#L2902-L2938","documentation":"When rendering a diagram to SVG, the renderer iterates over objects and switches on their concrete type to draw shape-specific elements. If an object in the diagram's Icons/appendix/legend path is neither a known shape nor connection type, it returns this error rather than silently skipping it.","triggerScenarios":"Rendering a d2target.Diagram whose internal objects slice contains an unexpected type — typically from deserializing a diagram JSON produced by a different d2 version, or hand-crafted/modified diagram JSON.","commonSituations":"Caching rendered diagram JSON across version upgrades; custom tools that post-process d2 output JSON and inject objects; using d2svg.Render directly with an exported diagram that has an unrecognized object.","solutions":["Re-generate the diagram with the same terrastruct/d2 version used for rendering","Confirm the diagram JSON was not hand-edited or corrupted between compile and render","Check for mismatched d2target versions between your go.mod and any vendored renderer","If you control the objects, ensure every item is a *d2target.Shape or *d2target.Connection"],"exampleFix":"// before\nobj := customStruct{} // injected into diagram.Objects\n// after\nobj := &d2target.Shape{} // only supported object types in diagram","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isKnownObject(obj interface{}) bool {\n\tswitch obj.(type) {\n\tcase *d2target.Shape, *d2target.Connection:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":null,"preventionTips":["Render with the same d2 version that compiled the diagram","Never hand-edit serialized diagram JSON","Round-trip diagram JSON through the same module version in CI"],"tags":["svg","rendering","type-error"],"backgroundTag":"unknown-object-type","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}