[Solved] Unexpected character (at character) Error in Flutter
![[Solved] Unexpected character (at character) Error in Flutter](https://flutterdocs.dev/wp-content/uploads/flutterdocs-recovered/53adaf494dc89ef7196d73636eb2451b.webp)
In this post, we are going to show you how to solve “Unexpected character (at character)” error in Flutter/Dart. This error usually happens while decoding an invalid JSON string, or if you are getting invalid JSON from REST API in the Dio package.
Error Message:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═════════════════
The following FormatException was thrown building Builder:
Unexpected character (at character 25)This error happens when you are decoding invalid JSON with json.decode() or jsonDecode().
I/flutter (16776): DioError [DioErrorType.other]:
FormatException: Unexpected character (at character 1)
I/flutter (16776): <br />
I/flutter (16776): ^
I/flutter (16776):
I/flutter (16776): Source stack:
I/flutter (16776): #0 DioMixin.fetch (package:dio/src/dio_mixin.dart:473:35)
I/flutter (16776): #1 DioMixin.request (package:dio/src/dio_mixin.dart:468:12)
I/flutter (16776): #2 DioMixin.get (package:dio/src/dio_mixin.dart:55:12)This error happens when you are getting invalid JSON data from REST API. You need to remind, that Dio() package returns decoded objects from JSON strings.
Cause of Error:
The cause of this error is an invalid JSON string, either JSON string inside code lines or JSON string data fetched from REST API.
How to Solve the Error:
Case 1: If you are using jsonDecode() or json.decode() to decode the JSON string, you need to verify if the JSON string is invalid. For example:
String jsonstring = '{"error":false,"msg":"",aa"data":[{"name":"Canada","capital":"Ottawa"}]}';
var jsonobj = jsonDecode(jsonstring); // Runtime Error: Unexpected character (at character)To solve this error let’s verify if the JSON string is valid or not. You can use any JSON validator. I’m using https://jsonlint.com/ and I got the error like below when validating the JSON.
Error: Parse error on line 3:
...false, "msg": "", aa "data": [{ "nam
---------------------^
Expecting 'STRING', got 'undefined'Its means, I have an invalid JSON string, for example, there is “aa”, an unexpected character after “msg” in the JSON string above code.
You can escape this error with try…catch:
try{
var jsonobj = jsonDecode(jsonstring);
}catch(e){
print(e);
}Case 2: If you are fetching JSON string from REST API, or the internet, you may get an error message generated by the compiler in server-side while generating the JSON string. You can use API tester software to check if the REST API is returning valid JSON, For example:
Here, after checking the response from REST API with API tester software, I got the message like below:
![[Solved] Unexpected character (at character) Error in Flutter screenshot](/wp-content/uploads/flutterdocs-recovered/31857b449c407203749ae32dd0e7d64a.webp)
This means, we got the error from the server-side, and the error message is returned by REST API along with JSON string. You can resolve this error on a server-side script or, disable the error message on the compiler.
You can escape this error from Dio() like below:
try{
response = await dio.get(url);
}catch(e){
print(e);
}In this way, you can resolve the “Unexpected character (at character)” Error in Flutter/Dart. This syntax error typically occurs during JSON parsing; see our guide on generating structured JSON data and encoding and decoding strings in Dart.
Updated Flutter Example
Safe JSON Parsing & FormatException Error Guard
Key Implementation Details & Modern Differences:
- Safe Result Tuple: Returns a structured typed record (
{dynamic data, JsonParseFailure? failure}) preventing uncaught runtime exceptions. - HTML Error Page Detection: Explicitly catches server HTTP 500/502 HTML responses before JSON deserialization to diagnose misleading 'Unexpected character' errors.
- Offset Diagnostic: Pinpoints the character index and suggests actionable remediations (e.g. single-quote or trailing-comma fixes).
import 'dart:convert';
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: ModernJsonErrorDemo()));
}
/// Typed representation of a JSON parsing error with context and remediation advice.
class JsonParseFailure {
final String rawInput;
final String message;
final int? offset;
final String remediation;
const JsonParseFailure({
required this.rawInput,
required this.message,
this.offset,
required this.remediation,
});
@override
String toString() => 'JsonParseFailure: $message (offset: $offset)';
}
/// Robust JSON parsing utility that guards against typical format exceptions.
class SafeJsonParser {
/// Safely decodes [source] into structured JSON data or returns a [JsonParseFailure].
static ({dynamic data, JsonParseFailure? failure}) decode(String source) {
final trimmed = source.trim();
if (trimmed.isEmpty) {
return (
data: null,
failure: const JsonParseFailure(
rawInput: '',
message: 'Input string is empty',
remediation:
'Ensure server response body is not empty before parsing.',
),
);
}
// Guard against server returning HTML error pages (e.g., 500 / 404 / 502)
if (trimmed.startsWith('<!DOCTYPE html>') ||
trimmed.startsWith('<html>') ||
trimmed.startsWith('<head>')) {
return (
data: null,
failure: JsonParseFailure(
rawInput: trimmed,
offset: 0,
message: 'Received HTML document instead of JSON payload',
remediation: 'The server likely returned an unhandled error page (HTTP 500/502). Check API URL, status code, and Content-Type header.',
),
);
}
try {
final decoded = jsonDecode(trimmed);
return (data: decoded, failure: null);
} on FormatException catch (e) {
return (
data: null,
failure: JsonParseFailure(
rawInput: trimmed,
offset: e.offset,
message: e.message,
remediation: 'Verify JSON syntax: ensure strings and keys use double quotes, no trailing commas, and brackets match.',
),
);
} catch (e) {
return (
data: null,
failure: JsonParseFailure(
rawInput: trimmed,
message: e.toString(),
remediation: 'An unexpected error occurred during deserialization.',
),
);
}
}
}
class ModernJsonErrorDemo extends StatefulWidget {
const ModernJsonErrorDemo({super.key});
@override
State<ModernJsonErrorDemo> createState() => _ModernJsonErrorDemoState();
}
class _ModernJsonErrorDemoState extends State<ModernJsonErrorDemo> {
final TextEditingController _controller = TextEditingController();
dynamic _parsedData;
JsonParseFailure? _failure;
static const _sampleValid =
'{"id": 101, "title": "Flutter 3.47 Guide", "active": true}';
static const _sampleHtml =
'<!DOCTYPE html><html><body><h1>500 Internal Server Error</h1></body></html>';
static const _sampleBadQuotes = "{'id': 102, 'name': 'Single Quotes'}";
@override
void initState() {
super.initState();
_controller.text = _sampleValid;
_runParse();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _runParse() {
final result = SafeJsonParser.decode(_controller.text);
setState(() {
_parsedData = result.data;
_failure = result.failure;
});
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('Safe JSON Parser & Error Guard')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Input Payload:', style: theme.textTheme.titleMedium),
const SizedBox(height: 8),
TextField(
controller: _controller,
maxLines: 4,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter JSON or API response body...',
),
onChanged: (_) => _runParse(),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
OutlinedButton(
onPressed: () {
_controller.text = _sampleValid;
_runParse();
},
child: const Text('Valid JSON'),
),
OutlinedButton(
onPressed: () {
_controller.text = _sampleHtml;
_runParse();
},
child: const Text('HTML Error Page'),
),
OutlinedButton(
onPressed: () {
_controller.text = _sampleBadQuotes;
_runParse();
},
child: const Text('Single Quotes Error'),
),
],
),
const SizedBox(height: 24),
Text('Parser Status:', style: theme.textTheme.titleMedium),
const SizedBox(height: 8),
if (_failure != null)
Card(
color: theme.colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.error_outline,
color: theme.colorScheme.error,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_failure!.message,
key: const Key('json-error-message'),
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onErrorContainer,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 8),
Text(
'Remediation: ${_failure!.remediation}',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onErrorContainer,
),
),
],
),
),
)
else
Card(
color: theme.colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.check_circle_outline,
color: theme.colorScheme.primary,
),
const SizedBox(width: 8),
Text(
'Valid JSON Deserialized',
key: const Key('json-success-message'),
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 8),
Text(
'Parsed: $_parsedData',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onPrimaryContainer,
),
),
],
),
),
),
],
),
),
);
}
}
Document History & Archival Record
Original reference snapshot archived March 22, 2023. Updated working solution verified on modern Flutter & Dart SDKs.