- {{ file.label ?? file.name }}
+ {{ file.label || file.name }}
@if (file && validateIndividually() && file.helper; as helper) {
{
+ output[issue.errorKey] = issue.value;
+ });
+
+ return output;
};
- validateFiles(files: FileDropzone[]): FileDropzoneErrors {
- const issues: FileDropzoneErrors = [];
+ validateFiles(files: FileDropzone[]): ValidatorError[] {
+ const issues: ValidatorError[] = [];
files.forEach((file) => {
this.validators().forEach((validator) => {
const error = validator(
@@ -272,14 +279,17 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
this._translationService.translate.bind(this._translationService),
);
- if (error && error.code) {
+ if (error?.value) {
issues.push(error);
file.fileStatus = "invalid";
+ const message = error.value.message;
file.helper = {
- text: error.message,
+ text: message,
type: "error",
};
- } else if (!issues.find((issue) => issue.fileName === file.name)) {
+ } else if (
+ !issues.find((issue) => issue.value.fileName === file.name)
+ ) {
file.fileStatus = "valid";
file.helper = undefined;
}
@@ -288,17 +298,6 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
return issues;
}
- fileClasses = (file: FileDropzone): string => {
- const classList = ["tedi-file-dropzone__file-item"];
- if (file.className) {
- classList.push(...file.className);
- }
- if (file.fileStatus != "none") {
- classList.push(`tedi-file-dropzone__file-item--${file.fileStatus}`);
- }
- return classList.join(" ");
- };
-
tooltipClasses = (file: FileDropzone): string => {
const classes = ["tedi-file-dropzone__tooltip"];
classes.push(`tedi-file-dropzone__tooltip--${file.helper?.type || "hint"}`);
@@ -374,17 +373,18 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
}
private _currentErrorState(): string | undefined {
- const errors = this._ngControl.control?.errors?.["tediFileDropzone"];
+ const errors: FormControlErrors[] =
+ this._ngControl.control?.errors?.["tediFileDropzone"];
if (errors && !this.validateIndividually()) {
- const dropzoneErrors: FileDropzoneErrors = errors;
+ const dropzoneErrors = errors;
return dropzoneErrors.map((error) => error.message).join(" ");
}
return undefined;
}
private _getNewState(): ValidationState {
- const errors: FileDropzoneErrors =
+ const errors: FormControlErrors[] =
this._ngControl.control?.errors?.["tediFileDropzone"];
if (this._ngControl.control?.touched) {
return "none";
diff --git a/community/components/form/file-dropzone/file-dropzone.stories.ts b/community/components/form/file-dropzone/file-dropzone.stories.ts
index d88c94ad..8ff09e32 100644
--- a/community/components/form/file-dropzone/file-dropzone.stories.ts
+++ b/community/components/form/file-dropzone/file-dropzone.stories.ts
@@ -7,7 +7,12 @@ import {
StoryObj,
} from "@storybook/angular";
import { validateFileSize, validateFileType } from "./utils";
-import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms";
+import {
+ FormControl,
+ FormGroup,
+ ReactiveFormsModule,
+ Validators,
+} from "@angular/forms";
import { ButtonComponent } from "tedi/components";
/**
@@ -142,7 +147,9 @@ const Template = (args: StoryArgs) => `
export const Default: Story = {
render: (args) => {
const form = new FormGroup({
- files: new FormControl(null),
+ files: new FormControl(null, {
+ validators: [Validators.maxLength(1)],
+ }),
});
const logValue = () => {
@@ -150,7 +157,7 @@ export const Default: Story = {
form.get("files")?.value,
form.controls.files,
form.controls,
- form.controls.files.errors
+ form.controls.files.errors,
);
};
return {
@@ -164,6 +171,11 @@ export const Default: Story = {
},
};
+export const MaxFileAmount: Story = {
+ ...Default,
+
+};
+
/** Replaces any same-name files, when usually it indexes them. */
export const Replace: Story = {
render: (args) => ({ template: Template(args), props: args }),
diff --git a/community/components/form/file-dropzone/types.ts b/community/components/form/file-dropzone/types.ts
index 9df7d878..6c1bc082 100644
--- a/community/components/form/file-dropzone/types.ts
+++ b/community/components/form/file-dropzone/types.ts
@@ -59,13 +59,22 @@ export class FileDropzone {
}
}
-export interface FileDropzoneError {
+// formcontrol error should be {key: {fileName: string, code: string, message: string}}
+
+export type FormControlErrors = {
+ [key: string]: SingleFileDropzoneError;
+};
+
+export type ValidatorError = {
+ errorKey: string;
+ value: SingleFileDropzoneError;
+};
+
+export type SingleFileDropzoneError = {
fileName: string;
code: string;
message: string;
-}
-
-export type FileDropzoneErrors = FileDropzoneError[];
+};
export enum FileDropzoneErrorCode {
FILE_TOO_LARGE = "file-too-large",
@@ -81,7 +90,7 @@ export type DropzoneValidatorFunction = (
acceptFileTypes: string,
file: FileDropzone,
standard: SizeDisplayStandard,
- translate: (key: string, ...args: unknown[]) => string
-) => FileDropzoneError | undefined;
+ translate: (key: string, ...args: unknown[]) => string,
+) => ValidatorError | undefined;
export type SizeDisplayStandard = "SI" | "IEC";
diff --git a/community/components/form/file-dropzone/utils.ts b/community/components/form/file-dropzone/utils.ts
index 35c1b989..90653774 100644
--- a/community/components/form/file-dropzone/utils.ts
+++ b/community/components/form/file-dropzone/utils.ts
@@ -1,14 +1,14 @@
import { IECFileSize, SIFileSize } from "./constants";
import {
+ DropzoneValidatorFunction,
FileDropzone,
- FileDropzoneError,
FileDropzoneErrorCode,
SizeDisplayStandard,
} from "./types";
export function formatBytes(
bytes: number,
- standard: SizeDisplayStandard
+ standard: SizeDisplayStandard,
): string {
let kB: number = 0;
let MB: number = 0;
@@ -45,55 +45,58 @@ export function getDefaultHelpers(
accept: string,
maxSize: number,
standard: SizeDisplayStandard,
- translate?: (key: string, ...args: unknown[]) => string
+ translate?: (key: string, ...args: unknown[]) => string,
): string {
if (!translate)
throw new Error(
- "Translate function is required to generate default helpers."
+ "Translate function is required to generate default helpers.",
);
const textArray = [];
if (accept) {
textArray.push(
- `${translate("file-upload.accept")} ${accept.replaceAll(",", ", ")}`
+ `${translate("file-upload.accept")} ${accept.replaceAll(",", ", ")}`,
);
}
if (maxSize) {
textArray.push(
- `${translate("file-upload.max-size")} ${formatBytes(maxSize, standard)}`
+ `${translate("file-upload.max-size")} ${formatBytes(maxSize, standard)}`,
);
}
return textArray.filter(Boolean).join(". ");
}
-export function validateFileSize(
+export const validateFileSize: DropzoneValidatorFunction = (
maxSize: number,
acceptFileTypes: string,
file: FileDropzone,
standard: SizeDisplayStandard,
- translate: (key: string, ...args: unknown[]) => string
-): FileDropzoneError | undefined {
+ translate: (key: string, ...args: unknown[]) => string,
+) => {
if (maxSize && file.size > maxSize) {
const maxSizeMB = formatBytes(maxSize, standard);
return {
- code: FileDropzoneErrorCode.FILE_TOO_LARGE,
- fileName: file.name,
- message: translate(
- `file-upload.size-rejected-extended`,
- file.name,
- maxSizeMB
- ),
+ errorKey: "file-too-large",
+ value: {
+ code: FileDropzoneErrorCode.FILE_TOO_LARGE,
+ fileName: file.name,
+ message: translate(
+ `file-upload.size-rejected-extended`,
+ file.name,
+ maxSizeMB,
+ ),
+ },
};
}
return undefined;
-}
+};
-export function validateFileType(
+export const validateFileType: DropzoneValidatorFunction = (
maxSize: number,
acceptFileTypes: string,
file: FileDropzone,
standard: SizeDisplayStandard,
- translate: (key: string, ...args: unknown[]) => string
-): FileDropzoneError | undefined {
+ translate: (key: string, ...args: unknown[]) => string,
+) => {
if (acceptFileTypes) {
const validTypes = acceptFileTypes
.split(",")
@@ -114,15 +117,18 @@ export function validateFileType(
if (!matches) {
return {
- code: FileDropzoneErrorCode.INVALID_FILE_TYPE,
- fileName: file.name,
- message: translate(
- "file-upload.extension-rejected-extended",
- file.name,
- acceptFileTypes
- ),
+ errorKey: "invalid-file-type",
+ value: {
+ code: FileDropzoneErrorCode.INVALID_FILE_TYPE,
+ fileName: file.name,
+ message: translate(
+ "file-upload.extension-rejected-extended",
+ file.name,
+ acceptFileTypes,
+ ),
+ },
};
}
}
return undefined;
-}
+};
From 334e42ed8448bd063385e3c85968da69feebedf6 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Fri, 30 Jan 2026 03:18:11 +0200
Subject: [PATCH 2/3] fix(file-dropzone): heavy refactor of validation into
actual angular validators #66
---
.../file-dropzone/file-dropzone.component.ts | 91 ++---------
.../file-dropzone/file-dropzone.stories.ts | 31 ++--
.../components/form/file-dropzone/types.ts | 24 +--
.../components/form/file-dropzone/utils.ts | 151 ++++++++++++------
4 files changed, 147 insertions(+), 150 deletions(-)
diff --git a/community/components/form/file-dropzone/file-dropzone.component.ts b/community/components/form/file-dropzone/file-dropzone.component.ts
index 301594a0..a888d903 100644
--- a/community/components/form/file-dropzone/file-dropzone.component.ts
+++ b/community/components/form/file-dropzone/file-dropzone.component.ts
@@ -14,11 +14,9 @@ import {
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import {
- AbstractControl,
ControlValueAccessor,
NgControl,
ReactiveFormsModule,
- ValidationErrors,
} from "@angular/forms";
import {
ClosingButtonComponent,
@@ -34,14 +32,12 @@ import {
} from "@tedi-design-system/angular/tedi";
import { CardComponent, CardContentComponent } from "../../cards";
import {
- DropzoneValidatorFunction,
FeedbackTextProps,
FileDropzone,
FileInputMode,
FormControlErrors,
SizeDisplayStandard,
ValidationState,
- ValidatorError,
} from "./types";
import {
formatBytes,
@@ -146,15 +142,6 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
* @default false
*/
uploadFolder = input(false);
- /**
- * Validation functions that can be used to validate files.
- * Each function should return a string error message if validation fails, or undefined if it passes
- * Validators are only added once during component initialization, otherwise use addAsyncValidators on the FormControl.
- */
- validators = input([
- validateFileSize,
- validateFileType,
- ]);
/**
* If true, shows the file dropzone as in a erroring state with red border.
* Overrides default validation state.
@@ -225,14 +212,23 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
constructor(@Self() public _ngControl: NgControl) {
this._ngControl.valueAccessor = this;
- console.log("loggin");
}
ngOnInit(): void {
this.addFiles(this.defaultFiles());
this._fileService.mode = this.mode;
const control = this._ngControl.control;
- control?.addAsyncValidators(this.runValidators);
+ control?.addValidators([
+ validateFileSize(
+ this.maxSize(),
+ this.sizeDisplayStandard(),
+ this._translationService.translate.bind(this._translationService),
+ ),
+ validateFileType(
+ this.accept(),
+ this._translationService.translate.bind(this._translationService),
+ ),
+ ]);
this._ngControl.control?.statusChanges
?.pipe(takeUntilDestroyed(this._destroyRef))
@@ -244,60 +240,6 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
this.uploadState.set(this._getNewState());
}
- runValidators = async (
- control: AbstractControl,
- ): Promise => {
- const controlFiles = control.value;
-
- if (!controlFiles || !controlFiles.length) {
- return null;
- }
-
- const issues = this.validateFiles(controlFiles);
-
- if (!issues.length) {
- return null;
- }
-
- const output: FormControlErrors = {};
- issues.forEach((issue) => {
- output[issue.errorKey] = issue.value;
- });
-
- return output;
- };
-
- validateFiles(files: FileDropzone[]): ValidatorError[] {
- const issues: ValidatorError[] = [];
- files.forEach((file) => {
- this.validators().forEach((validator) => {
- const error = validator(
- this.maxSize(),
- this.accept(),
- file,
- this.sizeDisplayStandard(),
- this._translationService.translate.bind(this._translationService),
- );
-
- if (error?.value) {
- issues.push(error);
- file.fileStatus = "invalid";
- const message = error.value.message;
- file.helper = {
- text: message,
- type: "error",
- };
- } else if (
- !issues.find((issue) => issue.value.fileName === file.name)
- ) {
- file.fileStatus = "valid";
- file.helper = undefined;
- }
- });
- });
- return issues;
- }
-
tooltipClasses = (file: FileDropzone): string => {
const classes = ["tedi-file-dropzone__tooltip"];
classes.push(`tedi-file-dropzone__tooltip--${file.helper?.type || "hint"}`);
@@ -373,23 +315,22 @@ export class FileDropzoneComponent implements ControlValueAccessor, OnInit {
}
private _currentErrorState(): string | undefined {
- const errors: FormControlErrors[] =
- this._ngControl.control?.errors?.["tediFileDropzone"];
+ const errors: FormControlErrors = this._ngControl.control?.errors;
if (errors && !this.validateIndividually()) {
- const dropzoneErrors = errors;
+ const dropzoneErrors = Object.values(errors);
+
return dropzoneErrors.map((error) => error.message).join(" ");
}
return undefined;
}
private _getNewState(): ValidationState {
- const errors: FormControlErrors[] =
- this._ngControl.control?.errors?.["tediFileDropzone"];
+ const errors: FormControlErrors = this._ngControl.control?.errors;
if (this._ngControl.control?.touched) {
return "none";
}
- if (errors?.length) {
+ if (errors) {
return "invalid";
}
return this.files().length > 0 ? "valid" : "none";
diff --git a/community/components/form/file-dropzone/file-dropzone.stories.ts b/community/components/form/file-dropzone/file-dropzone.stories.ts
index 8ff09e32..2e9d45ad 100644
--- a/community/components/form/file-dropzone/file-dropzone.stories.ts
+++ b/community/components/form/file-dropzone/file-dropzone.stories.ts
@@ -6,7 +6,6 @@ import {
moduleMetadata,
StoryObj,
} from "@storybook/angular";
-import { validateFileSize, validateFileType } from "./utils";
import {
FormControl,
FormGroup,
@@ -58,7 +57,6 @@ const meta: Meta = {
mode: "append",
name: "file-dropzone",
uploadFolder: false,
- validators: [validateFileSize, validateFileType],
hasError: false,
},
argTypes: {
@@ -117,11 +115,6 @@ const meta: Meta = {
uploadFolder: {
description: ` If true, allows uploading folders instead of just files. This enables the user to select a folder and upload all its contents. Default file browser behaviour will prevent upload of files in this state.`,
},
- validators: {
- control: false,
- description:
- "Validation functions that can be used to validate files. Each function should return a string error message if validation fails, or undefined if it passes.",
- },
hasError: {
description: `If true, shows the file dropzone as in a erroring state with red border.
Overrides default validation state.`,
@@ -171,14 +164,28 @@ export const Default: Story = {
},
};
+/** Limits the maximum amount of files to 1 */
export const MaxFileAmount: Story = {
- ...Default,
-
-};
+ render: (args) => {
+ const form = new FormGroup({
+ files: new FormControl(null, {
+ validators: [Validators.maxLength(1)],
+ }),
+ });
+ return {
+ template: Template(args),
+ props: { ...args, form },
+ };
+ },
+ args: {
+ inputId: "file-dropzone-form-control",
+ name: "file-form-control",
+ },
+};
/** Replaces any same-name files, when usually it indexes them. */
export const Replace: Story = {
- render: (args) => ({ template: Template(args), props: args }),
+ ...Default,
args: {
mode: "replace",
},
@@ -186,7 +193,7 @@ export const Replace: Story = {
/** Custom un-translated label text. */
export const WithHint: Story = {
- render: (args) => ({ template: Template(args), props: args }),
+ ...Default,
args: {
name: "file",
label: "Custom hint here",
diff --git a/community/components/form/file-dropzone/types.ts b/community/components/form/file-dropzone/types.ts
index 6c1bc082..b976dd77 100644
--- a/community/components/form/file-dropzone/types.ts
+++ b/community/components/form/file-dropzone/types.ts
@@ -59,20 +59,20 @@ export class FileDropzone {
}
}
-// formcontrol error should be {key: {fileName: string, code: string, message: string}}
-
-export type FormControlErrors = {
- [key: string]: SingleFileDropzoneError;
-};
-
-export type ValidatorError = {
+export type FormControlErrors =
+ | {
+ [key: string]: SingleFileDropzoneError;
+ }
+ | null
+ | undefined;
+
+export type DropzoneValidatorError = {
errorKey: string;
value: SingleFileDropzoneError;
};
export type SingleFileDropzoneError = {
fileName: string;
- code: string;
message: string;
};
@@ -85,12 +85,4 @@ export type FileInputMode = "append" | "replace";
export type ValidationState = "none" | "valid" | "invalid";
-export type DropzoneValidatorFunction = (
- maxSize: number,
- acceptFileTypes: string,
- file: FileDropzone,
- standard: SizeDisplayStandard,
- translate: (key: string, ...args: unknown[]) => string,
-) => ValidatorError | undefined;
-
export type SizeDisplayStandard = "SI" | "IEC";
diff --git a/community/components/form/file-dropzone/utils.ts b/community/components/form/file-dropzone/utils.ts
index 90653774..7aa793e8 100644
--- a/community/components/form/file-dropzone/utils.ts
+++ b/community/components/form/file-dropzone/utils.ts
@@ -1,6 +1,7 @@
+import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms";
import { IECFileSize, SIFileSize } from "./constants";
import {
- DropzoneValidatorFunction,
+ DropzoneValidatorError,
FileDropzone,
FileDropzoneErrorCode,
SizeDisplayStandard,
@@ -65,70 +66,126 @@ export function getDefaultHelpers(
return textArray.filter(Boolean).join(". ");
}
-export const validateFileSize: DropzoneValidatorFunction = (
- maxSize: number,
- acceptFileTypes: string,
+function sanitizeFileList(files: FileDropzone[] | unknown): FileDropzone[] {
+ if (!Array.isArray(files)) {
+ return [];
+ }
+ return files.filter((file) => file instanceof FileDropzone);
+}
+
+export const validateFileSize =
+ (
+ maxSize: number,
+ standard: SizeDisplayStandard,
+ translate: (key: string, ...args: unknown[]) => string,
+ ): ValidatorFn =>
+ (control: AbstractControl): ValidationErrors | null => {
+ const files = sanitizeFileList(control.value);
+
+ if (!files.length) {
+ return null;
+ }
+
+ const errors: ValidationErrors = {};
+ files.forEach((file) => {
+ const err = validateSingleFileSize(file, maxSize, standard, translate);
+ if (err) {
+ errors[err.errorKey] = err.value;
+ }
+ });
+
+ if (Object.keys(errors).length) {
+ return errors;
+ }
+
+ return null;
+ };
+
+const validateSingleFileSize = (
file: FileDropzone,
+ maxSize: number,
standard: SizeDisplayStandard,
translate: (key: string, ...args: unknown[]) => string,
-) => {
+): DropzoneValidatorError | null => {
if (maxSize && file.size > maxSize) {
- const maxSizeMB = formatBytes(maxSize, standard);
return {
- errorKey: "file-too-large",
+ errorKey: FileDropzoneErrorCode.FILE_TOO_LARGE,
value: {
- code: FileDropzoneErrorCode.FILE_TOO_LARGE,
fileName: file.name,
message: translate(
- `file-upload.size-rejected-extended`,
+ "file-upload.size-rejected-extended",
file.name,
- maxSizeMB,
+ formatBytes(maxSize, standard),
),
},
};
}
- return undefined;
+ return null;
};
-export const validateFileType: DropzoneValidatorFunction = (
- maxSize: number,
- acceptFileTypes: string,
+export const validateFileType =
+ (
+ acceptFileTypes: string,
+ translate: (key: string, ...args: unknown[]) => string,
+ ): ValidatorFn =>
+ (control: AbstractControl): ValidationErrors | null => {
+ const files = sanitizeFileList(control.value);
+
+ if (!files.length) {
+ return null;
+ }
+
+ if (acceptFileTypes) {
+ const errors: ValidationErrors = {};
+
+ files.forEach((file) => {
+ const err = validateSingleFileType(file, acceptFileTypes, translate);
+ if (err) {
+ errors[err.errorKey] = err.value;
+ }
+ });
+
+ if (Object.keys(errors).length) {
+ return errors;
+ }
+ }
+ return null;
+ };
+
+const validateSingleFileType = (
file: FileDropzone,
- standard: SizeDisplayStandard,
+ acceptFileTypes: string,
translate: (key: string, ...args: unknown[]) => string,
-) => {
- if (acceptFileTypes) {
- const validTypes = acceptFileTypes
- .split(",")
- .map((type) => type.trim().toLowerCase());
-
- const fileType = file.type.toLowerCase();
- const fileName = file.name.toLowerCase();
-
- const matches = validTypes.some((type) => {
- if (type.startsWith(".")) {
- return fileName.endsWith(type);
- }
- if (type.endsWith("/*")) {
- return fileType.startsWith(type.replace("/*", ""));
- }
- return fileType === type;
- });
+): DropzoneValidatorError | null => {
+ const validTypes = acceptFileTypes
+ .split(",")
+ .map((type) => type.trim().toLowerCase());
+
+ const fileType = file.type.toLowerCase();
+ const fileName = file.name.toLowerCase();
- if (!matches) {
- return {
- errorKey: "invalid-file-type",
- value: {
- code: FileDropzoneErrorCode.INVALID_FILE_TYPE,
- fileName: file.name,
- message: translate(
- "file-upload.extension-rejected-extended",
- file.name,
- acceptFileTypes,
- ),
- },
- };
+ const matches = validTypes.some((type) => {
+ if (type.startsWith(".")) {
+ return fileName.endsWith(type);
}
+ if (type.endsWith("/*")) {
+ return fileType.startsWith(type.replace("/*", ""));
+ }
+ return fileType === type;
+ });
+
+ if (!matches) {
+ return {
+ errorKey: FileDropzoneErrorCode.INVALID_FILE_TYPE,
+ value: {
+ fileName: file.name,
+ message: translate(
+ "file-upload.extension-rejected-extended",
+ file.name,
+ acceptFileTypes,
+ ),
+ },
+ };
}
- return undefined;
+ return null;
};
From 42dcd52afc19cfa4091ffe032f3e62a2b7ba71cf Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Fri, 30 Jan 2026 11:47:33 +0200
Subject: [PATCH 3/3] fix(file-dropzone): review request early maxSize return
#66
---
community/components/form/file-dropzone/utils.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/community/components/form/file-dropzone/utils.ts b/community/components/form/file-dropzone/utils.ts
index 7aa793e8..45fa29b5 100644
--- a/community/components/form/file-dropzone/utils.ts
+++ b/community/components/form/file-dropzone/utils.ts
@@ -80,6 +80,9 @@ export const validateFileSize =
translate: (key: string, ...args: unknown[]) => string,
): ValidatorFn =>
(control: AbstractControl): ValidationErrors | null => {
+ if (maxSize === 0) {
+ return null;
+ }
const files = sanitizeFileList(control.value);
if (!files.length) {
@@ -107,7 +110,7 @@ const validateSingleFileSize = (
standard: SizeDisplayStandard,
translate: (key: string, ...args: unknown[]) => string,
): DropzoneValidatorError | null => {
- if (maxSize && file.size > maxSize) {
+ if (file.size > maxSize) {
return {
errorKey: FileDropzoneErrorCode.FILE_TOO_LARGE,
value: {