Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Release notes are available on [github][notes].
- Error messages now include the variable name for easier debugging
- [fix] `load(isOptional: true)` no longer discards successfully loaded base file when an override file is missing or empty (fixes #70, #93, #101, #125)
- [fix] `clean()` now resets `isInitialized` to `false`, so accessing `env` after `clean()` correctly throws `NotInitializedError`
- [fix] Error classes (`NotInitializedError`, `FileNotFoundError`, `EmptyEnvFileError`) now include informative messages in `toString()` instead of the unhelpful `Instance of 'ClassName'` (fixes #72, #127; improves diagnostics for #59, #89)
- `FileNotFoundError` and `EmptyEnvFileError` now carry the filename when available

### Note on error message improvements
`NotInitializedError`, `FileNotFoundError`, and `EmptyEnvFileError` now override `toString()` with actionable messages (e.g., `FileNotFoundError: Environment file ".env" not found. Ensure the file exists and is listed under assets in pubspec.yaml.`). This is **not a breaking change** — the class names and hierarchy are unchanged, so existing `on FileNotFoundError` catch clauses continue to work. `FileNotFoundError` now accepts an optional positional `filename` parameter, and `EmptyEnvFileError` accepts an optional named `filename` parameter; both default to `null` for backward compatibility.

### Note on release-build behavior change
In **debug mode**, behavior is unchanged — `AssertionError` was thrown before, `AssertionError` is thrown now.
Expand Down
4 changes: 2 additions & 2 deletions lib/src/dotenv.dart
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,11 @@ class DotEnv {
WidgetsFlutterBinding.ensureInitialized();
var envString = await rootBundle.loadString(filename);
if (envString.isEmpty) {
throw EmptyEnvFileError();
throw EmptyEnvFileError(filename: filename);
}
return envString.split('\n');
} on FlutterError {
throw FileNotFoundError();
throw FileNotFoundError(filename);
}
}

Expand Down
29 changes: 26 additions & 3 deletions lib/src/errors.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
class NotInitializedError extends Error {}
class NotInitializedError extends Error {
@override
String toString() =>
'NotInitializedError: DotEnv has not been initialized. '
'Call load() or loadFromString() before accessing env variables.';
}

class FileNotFoundError extends Error {}
class FileNotFoundError extends Error {
final String? filename;
FileNotFoundError([this.filename]);

class EmptyEnvFileError extends Error {}
@override
String toString() => filename != null
? 'FileNotFoundError: Environment file "$filename" not found. '
'Ensure the file exists and is listed under assets in pubspec.yaml.'
: 'FileNotFoundError: Environment file not found. '
'Ensure the file exists and is listed under assets in pubspec.yaml.';
}

class EmptyEnvFileError extends Error {
final String? filename;
EmptyEnvFileError({this.filename});

@override
String toString() => filename != null
? 'EmptyEnvFileError: Environment file "$filename" is empty.'
: 'EmptyEnvFileError: The provided env string is empty.';
}
82 changes: 82 additions & 0 deletions test/errors_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('NotInitializedError', () {
test('toString contains actionable guidance', () {
final error = NotInitializedError();
expect(error.toString(), contains('NotInitializedError'));
expect(error.toString(), contains('load()'));
expect(error.toString(), contains('loadFromString()'));
});

test('is thrown when accessing env before initialization', () {
final env = DotEnv();
expect(() => env.env, throwsA(isA<NotInitializedError>()));
});

test('thrown error message does not reference global singleton', () {
final env = DotEnv();
try {
env.env;
fail('should have thrown');
} on NotInitializedError catch (e) {
expect(e.toString(), contains('load()'));
expect(e.toString(), isNot(contains('dotenv.load')));
}
});
});

group('FileNotFoundError', () {
test('toString contains the filename when provided', () {
final error = FileNotFoundError('.env');
expect(error.toString(), contains('FileNotFoundError'));
expect(error.toString(), contains('.env'));
expect(error.toString(), contains('pubspec.yaml'));
});

test('toString provides generic message when no filename given', () {
final error = FileNotFoundError();
expect(error.toString(), contains('FileNotFoundError'));
expect(error.toString(), contains('not found'));
});

test('filename field is accessible', () {
expect(FileNotFoundError('.env.production').filename, '.env.production');
expect(FileNotFoundError().filename, isNull);
});
});

group('EmptyEnvFileError from loadFromString', () {
test('thrown error message describes empty string context', () {
final env = DotEnv();
try {
env.loadFromString(envString: '');
fail('should have thrown');
} on EmptyEnvFileError catch (e) {
expect(e.toString(), contains('empty'));
expect(e.filename, isNull);
}
});
});

group('EmptyEnvFileError', () {
test('toString contains the filename when provided', () {
final error = EmptyEnvFileError(filename: '.env');
expect(error.toString(), contains('EmptyEnvFileError'));
expect(error.toString(), contains('.env'));
expect(error.toString(), contains('empty'));
});

test('toString provides generic message when no filename given', () {
final error = EmptyEnvFileError();
expect(error.toString(), contains('EmptyEnvFileError'));
expect(error.toString(), contains('empty'));
});

test('filename field is accessible', () {
expect(EmptyEnvFileError(filename: '.env').filename, '.env');
expect(EmptyEnvFileError().filename, isNull);
});
});
}
Loading