{"record":{"id":"dd743182429f94c7","repo":"marmelab/react-admin","slug":"children-of-translatableinputs-must-have-a-source","errorCode":null,"errorMessage":"Children of TranslatableInputs must have a source","messagePattern":"Children of TranslatableInputs must have a source","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ra-ui-materialui/src/input/TranslatableInputsTabContent.tsx","lineNumber":45,"sourceCode":"    const props = useThemeProps({\n        props: inProps,\n        name: PREFIX,\n    });\n    const { children, groupKey = '', locale, ...other } = props;\n    const { selectedLocale, getRecordForLocale } = useTranslatableContext();\n    const parentSourceContext = useSourceContext();\n    const record = useRecordContext(props);\n\n    // The SourceContext will be read by children of TranslatableInputs to compute their composed source and label\n    //\n    // <TranslatableInputs locales={['en', 'fr']} /> => SourceContext is \"fr\"\n    //     <TextInput source=\"description\" /> => final source for this input will be \"description.fr\"\n    // </TranslatableInputs>\n    const sourceContext = useMemo(\n        () => ({\n            getSource: (source: string) => {\n                if (!source) {\n                    throw new Error(\n                        'Children of TranslatableInputs must have a source'\n                    );\n                }\n                return parentSourceContext.getSource(`${source}.${locale}`);\n            },\n            getLabel: (source: string) => {\n                return parentSourceContext.getLabel(source);\n            },\n        }),\n        [locale, parentSourceContext]\n    );\n\n    // As fields rely on the RecordContext to get their values and have no knowledge of the locale,\n    // we need to create a new record with the values for the current locale only\n    // Given the record { title: { en: 'title_en', fr: 'title_fr' } } and the locale 'fr',\n    // the record for the locale 'fr' will be { title: 'title_fr' }\n    const recordForLocale = useMemo(\n        () => getRecordForLocale(record, locale),","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/marmelab/react-admin/blob/051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0/packages/ra-ui-materialui/src/input/TranslatableInputsTabContent.tsx#L27-L63","documentation":"TranslatableInputsTabContent builds a source context that suffixes each child input's source with the locale (e.g. 'description' becomes 'description.fr'). The getSource callback throws when a child reports an empty source, because there is nothing to namespace — every child of TranslatableInputs must declare a source.","triggerScenarios":"Placing an input without a source inside <TranslatableInputs> (e.g. a bare <SelectInput choices={...} />, a checkbox-only component, or a wrapper that strips source); rendering a component that calls getSource('') internally.","commonSituations":"Adding decorative/custom inputs inside TranslatableInputs; components that compute their source dynamically and yield an empty string; misusing TranslatableInputs around non-field components.","solutions":["Give every child input a source prop (e.g. <TextInput source=\"description\" />).","Move inputs that legitimately have no source outside of <TranslatableInputs>.","For custom inputs, ensure they forward a non-empty source to the translatable context."],"exampleFix":"// before\n<TranslatableInputs locales={['en', 'fr']}>\n  <SelectInput choices={[{ id: 1, name: 'One' }]} />\n</TranslatableInputs>\n\n// after\n<TranslatableInputs locales={['en', 'fr']}>\n  <SelectInput source=\"status\" choices={[{ id: 1, name: 'One' }]} />\n</TranslatableInputs>","handlingStrategy":"validation","validationCode":"React.Children.forEach(children, child => {\n  if (React.isValidElement(child) && !child.props.source) {\n    throw new Error('Children of TranslatableInputs must have a source');\n  }\n});","typeGuard":"const hasSource = (c: React.ReactElement): c is React.ReactElement<{ source: string }> =>\n  typeof (c.props as { source?: string }).source === 'string' &&\n  (c.props as { source?: string }).source !== '';","tryCatchPattern":null,"preventionTips":["Audit every child of TranslatableInputs for a source prop","Keep non-translatable, source-less components outside TranslatableInputs","Ensure custom inputs always forward a non-empty source"],"tags":["react-admin","i18n","translatable-inputs","children-validation"],"backgroundTag":"missing-required-prop","analyzedSha":"051f511bb0afb5ea565c2d3728bf4dab0a6fa5e0","analyzedAt":"2026-08-30T02:28:14.926Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}