{"record":{"id":"49cd714088d42018","repo":"plotly/plotly.js","slug":"unrecognized-full-object-value","errorCode":null,"errorMessage":"unrecognized full object value","messagePattern":"unrecognized full object value","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/plot_api/plot_api.js","lineNumber":2089,"sourceCode":"        // and order-independence for add/remove/edit all together in\n        // one relayout call\n        var containerArrayMatch = manageArrays.containerArrayMatch(ai);\n        if (containerArrayMatch) {\n            arrayStr = containerArrayMatch.array;\n            i = containerArrayMatch.index;\n            var propStr = containerArrayMatch.property;\n            var updateValObject = valObject || { editType: 'calc' };\n\n            if (i !== '' && propStr === '') {\n                // special handling of undoit if we're adding or removing an element\n                // ie 'annotations[2]' which can be {...} (add) or null,\n                // does not work when replacing the entire array\n                if (manageArrays.isAddVal(vi)) {\n                    undoit[ai] = null;\n                } else if (manageArrays.isRemoveVal(vi)) {\n                    undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i];\n                } else {\n                    Lib.warn('unrecognized full object value', aobj);\n                }\n            }\n            editTypes.update(flags, updateValObject);\n\n            // prepare the edits object we'll send to applyContainerArrayChanges\n            if (!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};\n            var objEdits = arrayEdits[arrayStr][i];\n            if (!objEdits) objEdits = arrayEdits[arrayStr][i] = {};\n            objEdits[propStr] = vi;\n\n            delete aobj[ai];\n        } else if (pleaf === 'reverse') {\n            // handle axis reversal explicitly, as there's no 'reverse' attribute\n\n            if (parentIn.range) parentIn.range.reverse();\n            else {\n                doextra(ptrunk + '.autorange', true);\n                parentIn.range = [1, 0];","sourceCodeStart":2071,"sourceCodeEnd":2107,"githubUrl":"https://github.com/plotly/plotly.js/blob/1d090e0b5ffb8d0fdf6e0e4ff51d3f3cf67f1314/src/plot_api/plot_api.js#L2071-L2107","documentation":"During _relayout processing of whole-object updates to container arrays (e.g. 'annotations[2]' set to a single value), the value must be an add value ('add' or plain object) or a remove value (null/[0]) for plotly.js to compute an undo entry. Any other value is unrecognized: the change is not undone correctly and this warning is logged.","triggerScenarios":"Plotly.relayout(gd, 'annotations[2]', 'foo') or Plotly.relayout(gd, {'shapes[0]': 5}) - assigning a primitive/string to a full container index rather than an object, 'add', or null. Also storing removal sentinels as strings like 'remove' instead of null.","commonSituations":"Template-driven relayouts where a placeholder string was not substituted; diff libraries emitting primitives for removed entries; code written against an imagined 'remove'/'delete' keyword that plotly.js never supported; type coercion after JSON round-trips turning null into 'null'.","solutions":["Assign a plain object to replace the item: Plotly.relayout(gd, 'annotations[2]', {x: 1, y: 2})","Use 'add' to insert at that index: Plotly.relayout(gd, 'annotations[2]', 'add')","Use null (or [0]) to remove: Plotly.relayout(gd, 'annotations[2]', null)","Validate each value in the aobj with manageArrays.isAddVal/isRemoveVal-equivalent checks before calling relayout","For wholesale array replacement set the whole array instead of per-index: Plotly.relayout(gd, 'annotations', newArray)"],"exampleFix":"// before\nPlotly.relayout(gd, 'annotations[1]', 'remove');\n// after\nPlotly.relayout(gd, 'annotations[1]', null);","handlingStrategy":"validation","validationCode":"function isValidContainerIndexValue(v) {\n  return v === 'add' || v === null || (v && typeof v === 'object' && !Array.isArray(v));\n}\nfor (const [k, v] of Object.entries(spec)) {\n  if (/\\[\\d+\\]$/.test(k) && !isValidContainerIndexValue(v)) throw new TypeError(k + ' needs object, \\'add\\', or null');\n}","typeGuard":"function isPlainObj(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }\nconst isAddVal = v => v === 'add' || isPlainObj(v);\nconst isRemoveVal = v => v === null;","tryCatchPattern":"try { await Plotly.relayout(gd, spec); } catch (e) { console.error('relayout with container index value rejected', spec, e); }","preventionTips":["Only assign objects, 'add', or null to annotations[i]-style keys","Sanitize template placeholders so strings like 'remove' never reach relayout","Round-trip specs through JSON carefully; 'null' as a string is not null","Prefer replacing the whole array (relayout with the array) for bulk changes"],"tags":["api-misuse","relayout","undo"],"backgroundTag":"invalid-update-value","analyzedSha":"1d090e0b5ffb8d0fdf6e0e4ff51d3f3cf67f1314","analyzedAt":"2026-09-02T22:03:39.906Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}